不定期收集的面试题

Part1

1.判断数据类型的函数
function type_fn(a){
    let result;
    if(typeof a == 'object'){
    result = (a instanceof Array)?'Array':'object'
    }else{
    result = typeof a
    }
    return result
}

说明:鉴于 数组和对象和null的typeof都是object 所以基于typeof 另外需要instanceof判断区分

//补充 也可以用:Object.prototype.toString.call(a).slice(8,-1)
2.原型链继承的例子
function Class(){
    this.slogan = function(){
        console.log('我来自班级1');
    }
}

function Classmate(name){
    this.name = function(){
        console.log('我是'+name)
    }
}

Classmate.prototype = new Class();

//实例化
var peo1 = new Classmate('jerry');

peo1.name();    //我是jerry
peo1.slogan();  //我来自班级1


3.描述new一个对象的过程
  1. 创建一个新的对象
  2. this指向新的对象
  3. 对this赋值 执行代码
  4. 返回this
function _new(){
    let target = {};//或者Object.create(null)。反正就是创建一个新对象
    //第一个参数是构造函数
    let [construtor,...args] = [...arguments];
    //执行[原型]连接,target是constructor的实例
    target.__proto__ = construtor.prototype;
    
    //执行构造函数,将属性或方法添加到创建的空对象上
    let result = constructor.apply(target,args);
    if(result && (typeof result == "object" || typeof result == "function")){
        //如果构造函数执行的结构返回的是一个对象,那么返回这个对象
        return result;
    }
    
    //返回创建的对象
    return target;
    
}
4.zepto(或其他框架下如何使用原型链)
/**
 * 原型链继承的例子
 * 功能: 1.获取元素改变或者获取他的innerhtml。
 *       2.添加事件。
 */

//构造函数
function Elem(id){
    this.elem = document.getElementById(id);
};
//在其原型上添加方法
Elem.prototype.html = function(val){
    var elem=this.elem;
    if(val){
        elem.innerHTML = val;
        return this;  //链式调用
    }else{
        return elem.innerHTML;
    }
}

Elem.prototype.on = function(type,fn){
    var elem=this.elem;
    elem.addEventListener(type,fn);
    return this;
}


var div=new Elem('app');
div.html(`<h1>你好javascript</h1>`).on('click',function(){
    alert('我是天使')
})

原文链接:https://blog.csdn.net/qq_24675001/article/details/82054443
5.说一下对变量提升的理解

当JavaScript执行过程进入新函数时,这个函数内被声明的所有变量都会被移动导到函数最开始的地方。这种现象叫做提升。且被提升的只有变量的声明。

函数执行时会先创建当前的上下文环境,其中这两点会产生“变量提升”的效果

  • 变量定义
  • 函数声明(注意和函数表达式的区别)
6.说明this几种不同的使用场景

作为构造函数时的执行 指向构造函数的对象

作为对象属性时的执行 指向对象

作为普通函数时的执行 指向window

call  bind apply 重新把this指向新对象

7.创建10个li,点击的时候弹出对应的序号
-----es5 闭包------
for (var i = 0; i < 10; i++) {
    (function (i) {
        var a = document.createElement('a')
        a.innerHTML = i + '<br>'
        a.addEventListener('click', function (e) {
            e.preventDefault()
            alert(i)
        })
        document.body.appendChild(a)
    })(i)
}
-------es6 块级作用域--------
var obj = document.getElementById("dd");

for(let i=0;i<3;i++){
    var li = document.createElement("li");
    li.innerHTML=i;
    dd.className = "li_"+i;
    obj.appendChild(li);
    li.addEventListener('click',function(e){
        alert(i)
    })
}
------代码片段替换createElement多次插入-----
var obj = document.getElementById("dd");
var fragment = document.createDocumentFragment();
//装载容器
for(let i=0;i<3;i++){
    var li = document.createElement("li");
    li.innerHTML="__我是"+i;
    dd.className = "li_"+i;
    fragment.appendChild(li)
}
obj.appendChild(fragment); //遍历后一次插入
obj.addEventListener('click',function(e){ //委派绑定事件
    const target = e.target;
    if(target.tagName === "LI"){  //注意这是大写
        console.log(target.innerText)
    }
})

8.如何理解作用域
*   自由变量
*   作用域链,即自由变量的查找
*   闭包的两个场景
9.实际开发中闭包的应用
// 闭包实际应用中主要用于封装变量,收敛权限
// 即把变量隐藏起来,不让外面拿到和修改
function isFirstLoad() {
    var _list = []

    return function (id) {
        if (_list.indexOf(id) >= 0) {
            return false
        } else {
            _list.push(id)
            return true
        }
    }
}

// 使用
var firstLoad = isFirstLoad()
firstLoad(10) // true          
firstLoad(10) // false
firstLoad(20) // true

// 在 isFirstLoad 函数外面,根本不可能修改掉 _list 的值
10.同步异步的区别是什么?分别举一个例子
同步会阻塞代码 而异步不会

alert 是同步 
setTimeout 是异步

eg:
console.log('1');
setTimeout(function(){
    console.log('2');
},200)
console.log('3');
-------------

console.log('1')
alert('2')
console.log('3');

11.一个关于setTimeout的笔试题
jq源码里面利用了js程序的单线程 写了setTimeout(function(){})来解决 等页面加载完;
------------------
console.log(1);
setTimeout(function(){
    console.log(2)
},0)
console.log(3)
setTimeout(function(){
    console.log(4)
},1000)
console.log(5)

>> 1 3 5 2 4 
12.前端使用异步的场景
 定时任务: setTimeout setInverval 
网络请求: ajax请求  动态<img>加载
事件绑定

13.Date 获取 2020-01-21格式的日期
function time(){
    var date = new Date();
    var year = date.getFullYear();
    var month = date.getMonth()+1;
    var day = date.getDate();
    return year+'-'+num_fn(month)+'-'+num_fn(day);
}
function num_fn(num){
    return num>=10? num : '0'+num
}
14.Math 获取随机字符串要求是长度一致的字符串格式
function randomAtr(){
         return Math.floor(Math.random()*1000000000)
         
         //或者
         //var num = Math.random()+'';
         //return num.slice(2,13)
}
15.能写一个能遍历对象和数组的的forEach函数
function forEach_fn(obj,fn){
var key;
if(obj instanceof Array){
    arr.forEach(function(item, index){
        fn(index, item)//遍历数组所有的元素
    })
}else{
    for(key in obj){
        fn(key, obj[key])
    }
}

}

16.DOM是哪种的基本数据结构

DOM树形结构

17.DOM操作的常用API有哪些

创建型api:createElementcreateTextNodecloneNodecreateDocumentFragment

页面修改型API
修改页面内容的api主要包括:appendChildinsertBeforeremoveChildreplaceChild

节点查询型API
document.getElementById
document.getElementsByTagName
document.getElementsByName
document.getElementsByClassName
document.querySelector和document.querySelectorAll:通过css选择器来查找元素,注意选择器要符合CSS选择器的规则,使用的深度优先搜索来获取元素

节点关系型api

1.父关系型api

  • parentNode:Element的父节点可能是Element,Document或DocumentFragment。
  • parentElement:与parentNode的区别在于,其父节点必须是一个Element,如果不是,则返回null

2.子关系型api

  • childNodes:返回一个即时的NodeList,表示元素的子节点列表,子节点可能会包含文本节点,注释节点等。
  • children:一个即时的HTMLCollection,子节点都是Element,IE9以下浏览器不支持。
  • firstNode:第一个子节点
  • lastNode:最后一个子节点
  • hasChildNodes方法:可以用来判断是否包含子节点。

3.兄弟关系型api

  • previousSibling:节点的前节点,如果该节点是第一个节点,则为null。
  • previousElementSibling:返回前元素节点,前一个节点必须是Element。
  • nextSibling:节点的后节点,如果该节点是最后一个节点,则为null。。
  • nextElementSibling:返回后元素节点,后一个节点必须是Element。

元素属性型api
setAttribute:根据名称和值修改元素的特性eg:element.setAttribute(name, value);
getAttribute:返回指定的特性名相应的特性值,如果不存在,则返回null或空字符串.

18.DOM节点的attr和property有何区别

property 只是一个JS对象的属性的修改
Attribute 是对html标签属性的修改

19.如何检测浏览器的类型

navigator.userAgent 加正则匹配

20.拆解url的各部分
href        完整的 URL。

protocol    当前 URL 的协议。
host        主机名和当前 URL 的端口号。
hostname    当前 URL 的主机名。
pathname    当前 URL 的路径部分。
port        当前 URL 的端口号。

hash        hash。从井号(#)开始)。
search        参数。从问号(?)开始的。
21.编写一个通用的事件监听函数
function bindEvent(elem,type,selector,fn){
    if(fn == null){
        fn = selector;
        selector = null;
    }
    elem.addEventListener(type,function(e){
        var target;
        if(selector){//有代理
            target = e.target;
            if(target.macthes(target)){
                fn.call(target,e)//重新指定this 并传参e
            }
        }else{//无代理
            fn(e);
        }
    })
    
}
22.描述事件冒泡的过程
沿着DOM树形结构,一层层网上冒泡,
一直到 最顶端或者出现阻止冒泡命令
e.stopPropagation();
冒泡的应用 代理
23.对于一个无限下拉加载图片的页面,如何给每个图片绑定事件
事件委派

24.手动编写一个ajax,不依赖第三方库

1.创建ajax对象
2.接收服务器
3.发送请求
4.接收返回值

var oAjax = new XMLHttpRequest();
oAjax.open("GET","text.json",false);
oAjax.send();
oAjax.onreadystatechange(function(){
    if(oAjax.readyState == 4){
        if(oAjax.status == 200){
            console.log(oAjax.responseText)
        }
    }
})
25.状态码的说明;
  • 0开头 未初始化 还没有调用open()方法;
  • 1开头 载入 已调用open()方法,正在发送请求
  • 2开头 载入完成 send()方法完成,已收到全部响应内容
  • 3开头 解析 正在解析响应内容
  • 4开头 完成 响应内容解析完成,可以再客户端调用
  • 5开头 服务器错误
26.跨域的几种实现方式
1. 服务器请求头修改
2. 重定向代理
3. jsonp
jsonp跨域 原生实现:
 <script>
    var script = document.createElement('script');
    script.type = 'text/javascript';

    // 传参并指定回调执行函数为onBack
    script.src = 'http://www.....:8080/login?user=admin&callback=onBack';
    document.head.appendChild(script);

    // 回调执行函数
    function onBack(res) {
        alert(JSON.stringify(res));
    }
 </script>
26.GET POST区别;
get 安全性差 容量低 有缓存 通过url传  一般用于获取数据

post 安全性高 容量高 无缓存  一般用于用户注册


27.描述cookies、sessionStrorage localStrorage的区别;
  • cookies

    • 存储量小 只有4K,
    • 所有http请求都带着,会影响获取资源的效率
    • 封装才能用 document.cookies
    • 可以设置过期时间;服务器也能访问设置;
  • sessionStrorage

    • 存储量大 最大5M;
    • 简单易用
    • 不能设置过期时间,关闭浏览器会删除
  • localStrorage

    • 存储量大 最大5M;
    • 简单易用
    • 需要手动删除缓存
localStorage.setItem("saveData", JSON.stringify(setData) ); //存值 补充:存储前先用JSON.stringify()将json对象转字符串
    
localStorage.getItem("saveData”) //取值 补充:JSON.parse(ss) 将json字符串转为 json格式
    
localStorage.removeItem("saveData”);//删值
28.从输入url到html的详细过程
浏览器根据DNS服务器得到域名的IP地址

向这个IP的机器发送https/https请求

服务器收到、处理并返回请求

浏览器得到返回内容
29.window.onload和DOMContentLoaded的区别
window.onload:页面的全部资源加载完才会执行,包括图片、视频等
DOMContentLoaded:DOM渲染完即可执行,此时图片、视频还没有加载完
30.性能优化
性能优化:
多使用内存、缓存或其他方法   
减少cpu计算、减少网络   

加载资源优化:
1静态资源的合并压缩 
2静态资源缓存 
3使用CDN让资源加载更快  
4使用SSR后端渲染,数据直接输出到HTML中

渲染优化:
1.CSS放前面 JS放后面
2.懒加载(图片懒加载、下拉加载更多)
3.减少DOM查询,对DOM查询做缓存  
4.减少DOM操作,多个操作尽量合并在一起执行
5.事件节流
6.尽早执行操作(如DOMContentLoaded)

Part2

1.移动端怎样处理 移动端 1px 被 渲染成 2px 问题
1 局部处理
meta标签中的 viewport属性 ,initial-scale 设置为 1 
rem 按照设计稿标准走,外加利用transfrome 的scale(0.5) 缩小一倍即可;
2 全局处理
meta标签中的 viewport属性 ,initial-scale 设置为 0.5
    rem 按照设计稿标准走即可
2.let var const
名字 特性说明
var 变量; 能重复声明; 函数级; 顶层对象的属性; 不限制修改
let 变量; 不能重复声明; 块级; 不属于顶层对象的属性; 可修改
const 声明和赋值必须是同时进行; 常量; 不能重复声明; 块级; 不属于顶层对象的属性; "不"可修改[ 实际上并非完全不可修改。const声明创建一个值的只读引用,对于基本类型不可修改,但如果是复合类型时,只要不修改引用,修改里面的值是可以的];
代码演示:
console.log(k_name); //变量提升;输出undefined
var k_name = "jerry";
console.log(k_name);//输出jerry

console.log('1',k_age);//不会变量提升;在定义之前报错不能用
let k_age = "12";
console.log('2',k_age);

//const基础类型时
const may_class = 3;
may_class = 4;//基础类型时 报错

//const复杂类型时
const all_class = [1,2,3];
all_class[0] = 9; //不报错
console.log(all_class);//修改成功 》[9, 2, 3] 
3.编写个简单的url解析和赋值替换

   var url = "http://witmax.cn/index.php?key0=0&key1=1&key2=2";

// 返回url的拼接对象
function parseQueryString(argu) {
    var str = argu.split('?')[1];
    var result = {};
    var temp = str.split('&');
    for (vari = 0; i < temp.length; i++) {
        var temp2 = temp[i].split('=');
        result[temp2[0]] = temp2[1];
    }
    return result; //url上的全部参

}

// 拓展一下 对页面url操作, 只有一个参 那么查找url上的参数并返回值, 如果有两个参数查找更新或删除参数
function urlSearch(argu ,newValue) {
    var str = window.location.search.slice(1);
    var result = {};
    var temp = str.split('&');
    for (vari = 0; i < temp.length; i++) {
        var temp2 = temp[i].split('=');
        result[temp2[0]] = temp2[1];
    }
    return result; //url上的全部参

    if (newValue !== undefined) {
        if (newValue !== null) {
            result[argu] = newValue;//更新值
        }else {
            delete result[argu];//删除值
        }
        // 更新参数后重新拼接 并替换url的链接
        var str = '';
        for(k in ee){
            str += (k + '=' + ee[k] + '&');
        }
        str = str.replace(/&$/,'');
        // var newUrl = window.decodeURIComponent(argu.split('?')[0]+'?'+str);
        var newUrl = window.location.href.split('?')[0]+'?'+str;
        window.history.pushState(null, document.title,newUrl);
        // return newUrl;
    }else{
        return result[argu];
    }
}

4.看看以下分别输出什么
 if([] == false){ alert(1)} //在比较前false转成0 [].toString()转成了空字符串 空字符串转成0, ;所以true
 if({} == false){ alert(2)}//在比较前false转成0, {}.toString()转成"[object Object]" 不等
 
 if([]){ alert(3)}
 
 //引用类型(数组 对象 函数)的比较并非值的比较:两个对象保含同样的属性及相同的值 它们是不相等的。 各个所以元素完全相等的两个数组也不相等。
 if([1] == [1]){ alert(4)}
 if({x:1} == {x:1} ){ alert(4)}
 

引用类型的比较均是引用的比较:当且仅当它们引用同一个基对象时,它们才相等 eg:

var a = [];
var b = a; //变量b引用同一个数组
b[0] = 1;
console.log(b); // [1]
console.log(a); // [1]

a === b; //true: a和b引用同一个数据,因此它们相等
 
 
###### 给定一个整数数组,找出其中两个数相加等于目标值
5.给定数组及目标值 nums = [2,7,11,15] ,target = 9 因为nums[0] + nums[1] = 2 + 7 = 9 返回[0,1]
var twoSum = function(nums, target) {
var cbArr = [];
for(var i = 0; i< nums.length; i++){
    if(cbArr.indexOf(i) == -1){ //除去已经在结果里面的 避免重复
        for(var k = 0; k< nums.length; k++){
            if((i !== k) && (nums[i]+ nums[k] == target)){
                cbArr.push(i,k);
            }
        }
    }
}
return cbArr;
};
6.字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。
eg:
输入: s = "lrloseumgh", k = 6
输出: "umghlrlose"

var reverseLeftWords = function(s, n) {     
    return s.slice(n).concat(s.slice(0,n))
};

8.给定一个分别输出一下的值:

function Foo(){
    getName = function(){alert(1)};
    return this;
}

Foo.getName = function(){alert(2)};
Foo.prototype.getName = function(){alert(3)};
var getName = function(){alert(4)};
function getName(){alert(5)};


Foo.getName(); 
getName();   
Foo().getName();     
getName();             
new Foo().getName();   

答案----
//2 4 1 1 3    
9.输出一下值 并分析:
function fn(a) {

    console.log(a)  
    var a = 123;    
    console.log(a)  
    function a() {}
    console.log(a)  
    
    var b = function() {
        console.log('bb','1')
    }
    console.log(b)  
    b(); 
    function b() {
        console.log('bb','2')
    }
}
fn(1)


 --------------------------
a的分析:
进入fn 虽然带参数a进去 但是 进到函数   
1 最开始a有声明式函数,会直接挂载在函数下,所以第一个打印是function;
2 第一个打印后马上就给a赋值了123, 所以第二个打印是123;
3 下一行 a的声明式函数比2执行还早 所以不影响 第三个打印是123;

--------------------------
b的分析:
1 b不管属于谁打印肯定是function;
2 最开始b有声明式函数,会直接挂载在函数下,然后var 重新赋值了b的函数 所以 输出是

答案:function  123 123    function  bb 1

10. 输出下面值
var i= 10;
function a(){
    i = 20;
    console.log(i); // 20;
    for(var i=0; i<6;i++){
        console.log(i) // 0-5
    }
    console.log(this.i); //10 :window.i  外部环境不能访问内部环境中的任何变量和函数
    console.log(i);     //6  : 当前的局部变量在循环时被赋值为6
}
a();
console.log(i) //10 :window.i  外部环境不能访问内部环境中的任何变量和函数
11.js函数add(1)(2)(3)(4)的累加

想法:写个闭包每次返回函数都带参累加,知道参数没有不再执行函数 则返回累加的和。
先写个暴力概念版 只要我写得够多就能'满足'题目要求:

function add(a){
    let sum = a;
    return function count_fn1(b){
        sum +=b;
        return function count_fn1(c){
            sum +=c;
                return function count_fn1(d){
                sum +=d;
                return sum;
            };
        };
    }
}
add(1)(2)(3)(4)//10
全然不顾灵活不灵活拓展不拓展~ 老子就是干

不 我们不能仅苟且的活着!要灵活要自由~

function add(a){
    let sum = a;
    return function count_fn(b){
        sum +=b;
        return count_fn(b);
    }
}
//改成递归 是可以灵活执行多少次函数达到累加。问题 输出的也是函数 没有把累计的值输出出来呀!

这时我们要了解一个知识点:当我们直接对函数使用alert()console.log()时,函数的toString()方法会被调用。而且函数的toString()方法是可以复写的

最终版:

function add(x) {
    var sum = x;
    var tmp = function (y) {
        sum = sum + y;
        return tmp;
    };
    tmp.toString = function () {
        return sum;
    };
    return tmp;
}
console.log(add(1)(2)(3));  //6
12. 输出是什么?
(() => {
  let x, y
  try {
    throw new Error()
  } catch (x) {
    (x = 1), (y = 2)
    console.log(x)
  }
  console.log(x)
  console.log(y)
})()

答案:
1 undefined 2;
分析:
catch 代码块接收参数 x。当我们传递参数时,这与之前定义的变量 x 不同 。这个 x 是属于 catch 块级作用域的且 默认了设置了等于1; 所以第一个打印1;
外面x依然没赋值;所以第二个打印`undefined`;
y最开始let声明了,后面赋值了2,所以第二个打印2;
13. 输出是什么?
let person = { name: "Lydia" };
const members = [person];
person = null;

console.log(members);


答案:[{ name: "Lydia" }];
分析:  
当设置两个对象彼此相等时,它们会通过 引用进行交互。但是当将`引用从一个变量分配至另一个变量时`,其实是执行了 复制 操作。 所以它们的引用并不同
14. 输出是什么?
const settings = {
  username: "lydiahallie",
  level: 19,
  health: 90
};

const data = JSON.stringify(settings, ["level", "health"]);
console.log(data);

答案:"{"level":19, "health":90}"
分析:
[JSON.stringify(value[, replacer[, space]])](https://www.runoob.com/js/javascript-json-stringify.html)//老子还有第二第三个参数可传哦。
// 参数一:转换的值
// 参数二:替代者。用于转换结果的函数或数组。
// 参数三:控制空格

JSON.stringify的第二个参数是 替代者(replacer). 替代者(replacer)可以是个函数或数组,用以控制哪些值如何被转换为字符串。
如果替代者(replacer)是个 数组 ,那么就只有包含在数组中的属性将会被转化为字符串。在本例中,只有名为"level" 和 "health" 的属性被包括进来, "username"则被排除在外。 data 就等于 "{"level":19, "health":90}”.
15.懒加载预加载

懒加载: 图片的懒加载做法 都是把链接存放在 data-src中,监听onscroll时,一个个加载出来;

预加载:将所有所需的资源提前请求加载到本地,这样后面在需要用到时就直接从缓存取资源。
实现预加载的几种办法:

//使用img标签 
<img src="http://pic26.nipic.com/20121213/6168183 0044449030002.jpg" style="display:none”/> 

------------
//js 使用Image对象
<script src="./myPreload.js"></script>

//myPreload.js文件
var image= new Image()
image.src="http://pic26.nipic.com/20121213/6168183 004444903000 2.jpg"

-------------
//使用PreloadJS库

-------------
//使用XMLHttpRequest对象,虽然存在跨域问题,但会精细控制预加载过程
var xmlhttprequest=new XMLHttpRequest();
xmlhttprequest.onreadystatechange=callback;
xmlhttprequest.onprogress=progressCallback;
xmlhttprequest.open("GET","http://image.baidu.com/mouse,jpg",true);
xmlhttprequest.send();
function callback(){
  if(xmlhttprequest.readyState==4&& xmlhttprequest.status==200){
    var responseText=xmlhttprequest.responseText;
  }else{
     console.log("Request was unsuccessful:"+xmlhttprequest.status);
  }
}
function progressCallback(e){
    e=e || event;
    if(e.lengthComputable){
    console.log("Received"+e.loaded+"of"+e.total+"bytes")
    }
}
16.break continue return throw分别是?
1.break 跳出循环
2.continue 跳出本次循环,进入下次循环
3.return 中止跳出当前函数
4.throw 异常信息;中止程序抛出异常,可用于中止程序 
17.内存泄漏

定义:内存泄漏指任何对象在您不再拥有或需要它之后仍然存在。
后果:变慢,崩溃,延迟大等:
哪些会造成:

  • 定时器未清除
  • 闭包、控制台日志、循环(在两个对象彼此引用且彼此保留时,就会产生一个循环)
  • dom清空时,还存在引用

避免策略

  • 减少不必要的全局变量,或者生命周期较长的对象,及时对无用的数据进行垃圾回收;
  • 注意程序逻辑,避免“死循环”之类的 ;
  • 避免创建过多的对象 原则:不用了的东西要及时归还。
  • 减少层级过多的引用
18.重绘和回流(重排)是什么,如何避免?

重绘:改变元素的样式。例如宽高颜色。
回流(重排):布局和样式都改变。

重排一定重绘,重绘不一定重排。

减少重绘和重排的方法

  • 不在布局信息改变时做DOM查询
  • 使用cssText或者className一次性改变属性
  • 使用fragment
  • 对于多次重排的元素,如动画,使用绝对定位脱离文档流,让他的改变不影响到其他元素
  • 使用translate替代top

Part3

1.BOM 和 DOM区别。

Bom是浏览器对象模型(Browser Object Mode)。包含:

  • Window对象:浏览器中打开的窗口, 顶层对象
  • Navigator对象:浏览器的相关信息
  • Screen对象:客户端显示屏幕的信息
  • History对象:用户在浏览器窗口中访问过的URL
  • Location对象:当前URL的信息

Dom是文档对象模型(Document Object Mode)。

  • Document对象,文档里所有的元素和节点。

BOM的window对象内可以对DOM进行引用

2.link和@import区别。

*link是html标签,没有兼容性。加载的时候同时渲染。
*import是css里面的引用方法,存在兼容性(IE5以上),@import的css会等页面加载结束后才加载。
*link权限高于import

3.link和@import区别。
阅读 486

推荐阅读
喈喱前端笔记
用户专栏

学习的付出 从不欺人

4 人关注
30 篇文章
专栏主页