共计 13830 个字符,预计需要花费 35 分钟才能阅读完成。
new 一个对象,这个过程中发生了什么?
var obj = new Object("name","sansan");
- 创建一个新对象,如:var obj = {};
- 新对象的_proto_属性指向构造函数的原型对象。
- 将构造函数的作用域赋值给新对象。(也所以 this 对象指向新对象)
- 执行构造函数内部的代码,将属性添加给 obj 中的 this 对象。
- 返回新对象 obj。
数组中常用的方法
改变原数组
splice() 添加 / 删除数组元素
let a = [1, 2, 3, 4, 5, 6, 7];
let item = a.splice(0, 3); // [1,2,3]
console.log(a); // [4,5,6,7]
// 从数组下标 0 开始,删除 3 个元素
let item1 = a.splice(0,3,'添加'); // [4,5,6]
console.log(a); // ['添加',7]
// 从数组下标 0 开始,删除 3 个元素,并添加元素 '添加'
sort() 数组排序
var array = [10, 1, 3, 4,20,4,25,8];
// 升序 a-b < 0 a 将排到 b 的前面,按照 a 的大小来排序的
array.sort(function(a,b){return a-b;});
console.log(array); // [1,3,4,4,8,10,20,25];
// 降序
array.sort(function(a,b){return b-a;});
console.log(array); // [25,20,10,8,4,4,3,1];
pop() 删除一个数组中的最后的一个元素
shift() 删除数组的第一个元素
push() 向数组的末尾添加元素
unshift()向数组开头添加元素
reverse()
let a = [1,2,3];
a.pop(); // 3, 返回被删除的元素
console.log(a); // [1,2]
a.shift(); // 1
console.log(a); // [2]
a.push("末尾添加"); // 2 , 返回数组长度
console.log(a) ; [2,"末尾添加"]
a.unshift("开头添加"); // 3
console.log(a); //["开头添加", 2, "末尾添加"]
a.reverse(); // ["末尾添加", 2, "开头添加"]
console.log(a) // ["末尾添加", 2, "开头添加"]
copyWithin() 指定位置的成员复制到其他位置
let a = ['zhang', 'wang', 'zhou', 'wu', 'zheng'];
// 1 位置开始被替换, 2 位置开始读取要替换的 5 位置前面停止替换
a.copyWithin(1, 2, 5);
// ["zhang", "zhou", "wu", "zheng", "zheng"]
fill() 填充数组
['a', 'b', 'c'].fill(7)
// [7, 7, 7]
['a', 'b', 'c'].fill(7, 1, 2)
// ['a', 7, 'c']
不改变原数组
join() 数组转字符串
let a= ['hello','world'];
let str2=a.join('+'); // 'hello+world'
cancat 合并两个或多个数组
let a = [1, 2, 3];
let b = [4, 5, 6];
// 连接两个数组
let newVal=a.concat(b); // [1,2,3,4,5,6]
ES6 扩展运算符 … 合并数组
let a = [2, 3, 4, 5]
let b = [4,...a, 4, 4]
console.log(a,b);
//[2, 3, 4, 5] [4,2,3,4,5,4,4]
indexOf() 查找数组是否存在某个元素,返回下标
let a=['啦啦',2,4,24,NaN]
console.log(a.indexOf('啦')); // -1
console.log(a.indexOf('啦啦')); // 0
ES7 includes() 查找数组是否包含某个元素 返回布尔
indexOf 方法不能识别 NaN
indexOf 方法检查是否包含某个值不够语义化,需要判断是否不等于 -1,表达不够直观
let a=['OB','Koro1',1,NaN];
a.includes(NaN); // true 识别 NaN
a.includes('Koro1',100); // false 超过数组长度 不搜索
a.includes('Koro1',-3); // true 从倒数第三个元素开始搜索
slice() 浅拷贝数组的元素
// 字符串也有一个 slice() 方法是用来提取字符串的,不要弄混了。let a = [{name: 'OBKoro1'}, {name: 'zhangsan'}];
let b = a.slice(0,1);
console.log(b, a);
// [{"name":"OBKoro1"}] [{"name":"OBKoro1"}]
a[0].name='改变原数组';
console.log(b,a);
// [{"name":"改变原数组"}] [{"name":"改变原数组"}]
遍历方法
forEach: 按升序为数组中含有效值的每一项执行一次回调函数
1. 无法中途退出循环,只能用 return 退出本次回调,进行下一次回调.
2. 它总是返回 undefined 值, 即使你 return 了一个值。
every 检测数组所有元素是否都符合判断条件
如果数组中检测到有一个元素不满足, 则整个表达式返回 false, 且元素不会再进行检测
function isBigEnough(element, index, array) {return element >= 10; // 判断数组中的所有元素是否都大于 10}
[12, 5, 8, 130, 44].every(isBigEnough); // false
[12, 54, 18, 130, 44].every(isBigEnough); // true
// 接受箭头函数写法
[12, 5, 8, 130, 44].every(x => x >= 10); // false
[12, 54, 18, 130, 44].every(x => x >= 10); // true
some 数组中的是否有满足判断条件的元素
如果有一个元素满足条件,则表达式返回 true, 剩余的元素不会再执行检测
filter 过滤原始数组,返回新数组
map 对数组中的每个元素进行处理,返回新的数组
reduce 为数组提供累加器,合并为一个值
reduce() 方法对累加器和数组中的每个元素(从左到右)应用一个函数,最终合并为一个值
// 数组求和
let sum = [0, 1, 2, 3].reduce(function (a, b) {return a + b;}, 0);
// 6
// 将二维数组转化为一维 将数组元素展开
let flattened = [[0, 1], [2, 3], [4, 5]].reduce((a, b) => a.concat(b),
[]);
// [0, 1, 2, 3, 4, 5]
ES6:find()& findIndex() 根据条件找到数组成员
这两个方法都可以识别 NaN, 弥补了 indexOf 的不足.
[1, 4, -5, 10,NaN].find((n) => Object.is(NaN, n));
// 返回元素 NaN
[1, 4, -5, 10].findIndex((n) => n < 0);
// 返回索引 2
ES6 keys()&values()&entries() 遍历键名、遍历键值、遍历键名 + 键值
for (let index of ['a', 'b'].keys()) {console.log(index);
}
// 0
// 1
for (let elem of ['a', 'b'].values()) {console.log(elem);
}
// 'a'
// 'b'
for (let [index, elem] of ['a', 'b'].entries()) {console.log(index, elem);
}
// 0 "a"
// 1 "b"
判断对象是否为空
const isEmpty = (obj) => obj.keys().length !== 0;
判断对象是否是数组
方法一:使用 Object.prototype.toString 来判断是否是数组
function isArray(obj){return Object.prototype.toString.call( obj) === '[object Array]';
}
这里使用 call 来使 toString 中 this 指向 obj。进而完成判断
方法二:使用原型链 来完成判断
function isArray(obj){return obj.__proto__ === Array.prototype;}
基本思想: 实例如果是某个构造函数构造出来的那么 它的__proto__是指向构造函数的 prototype 属性
继承有哪些方式?
- ES6 中的 class 继承
class Animal {constructor(name) {this.name = name;};
eat() {console.log(this.name + '正在吃东西');
};
}
// 继承动物类
class Cat extends Animal {catchMouse(){console.log(`${this.name}正在捉老鼠 `);
}
}
var cat= new Cat('Tom 猫');
cat.catchMouse();// Tom 猫正在捉老鼠
- 原型继承
function Animal(name) {this.name = name;}
Animal.prototype.eat= function () {console.log(this.name + '正在吃东西')
};
function Cat(furColor){this.furColor = furColor ;};
Cat.prototype = new Animal();
let tom = new Cat('black');
console.log(tom)
- 构造继承
- 寄生组合式继承
- 实例继承
call、apply、bind 之间的关系
bind,apply,call 三者都可以用来改变 this 的指向;
- 三者都可以用来改变
this
的指向 - 三者第一个参数都是 this 要指向的对象,也就是想指定的上下文,上下文就是指调用函数的那个对象。(点前的那个对象,没有就是全局 window)
- 三者都可以传参,但是 apply 是数组,而 call 是有顺序的传入
- bind 是返回对应函数,便于稍后调用;apply、call 则是立即执行
异步过程的构成要素有哪些?和异步过程是怎样的?
总结一下,一个异步过程通常是这样的:
- 主线程发起一个异步请求,相应的工作线程接收请求并告知主线程已收到(异步函数返回);
- 主线程可以继续执行后面的代码,同时工作线程执行异步任务;
- 工作线程完成工作后,通知主线程;
- 主线程收到通知后,执行一定的动作(调用回调函数)。
- 异步函数通常具有以下的形式:A(args…, callbackFn)。
- 它可以叫做异步过程的发起函数,或者叫做异步任务注册函数。
- args 和 callbackFn 是这个函数的参数。
所以,从主线程的角度看,一个异步过程包括下面两个要素:
- 发起函数(或叫注册函数) A。
- 回调函数 callbackFn。
它们都是在主线程上调用的,其中注册函数用来发起异步过程,回调函数用来处理结果。
举个具体的例子:
setTimeout(fn, 1000);
其中的 setTimeout 就是异步过程的发起函数,fn 是回调函数。
注意:前面说的形式 A(args…, callbackFn) 只是一种抽象的表示,并不代表回调函数一定要作为发起函数的参数。
例如:
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = xxx; // 添加回调函数
xhr.open('GET', url);
xhr.send(); // 发起函数
发起函数和回调函数就是分离的。
说说消息队列和事件循环
- 主线程在执行完当前循环中的所有代码后,就会到消息队列取出这条消息(也就是 message 函数),并执行它。
- 完成了工作线程对主线程的通知,回调函数也就得到了执行。
- 如果一开始主线程就没有提供回调函数,AJAX 线程在收到 HTTP 响应后,也就没必要通知主线程,从而也没必要往消息队列放消息。
异步过程的回调函数,一定不在当前的这一轮事件循环中执行
eval() 函数有什么用?
eval() 函数可计算某个字符串,并执行其中的的 JavaScript 代码。
字符串转数组
console.log(Object.values('abc')); // => ['a', 'b', 'c']
console.log(Array.from('abcd'));
console.log([...'abcd']);
console.log('abcd'.split(''));
console.log([].slice.call('abcd'));
构造元素重复的数组
可以使用 array.fill:
const arr = Array(6).fill(6);
console.log(arr); // => [6, 6, 6, 6, 6, 6]
构造元素连续的数组
// length 设置为你需要获取的整数的个数,index + 1 这个 1 可以替换为你要设置的第一个数的大小
const continuousIntegers = Array.from({length: 10}, (__, index) => index + 1);
console.log(continuousIntegers); // => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
JS 中如何将页面重定向到另一个页面?
- 使用
window.location.href =“https://www.onlineinterviewquestions.com/”
- 使用
location.replace:window.location.replace("https://www.onlineinterviewquestions.com/;")
JS 中的 Array.splice()
和Array.slice()
方法有什么区别
var arr=[0,1,2,3,4,5,6,7,8,9];// 设置一个数组
console.log(arr.slice(2,7));//2,3,4,5,6
console.log(arr.splice(2,7));//2,3,4,5,6,7,8
// 由此我们简单推测数量两个函数参数的意义,
slice(start,end)第一个参数表示开始位置, 第二个表示截取到的位置(不包含该位置)
splice(start,length)第一个参数开始位置, 第二个参数截取长度
var x=y=[0,1,2,3,4,5,6,7,8,9]
console.log(x.slice(2,5));//2,3,4
console.log(x);[0,1,2,3,4,5,6,7,8,9]原数组并未改变
// 接下来用同样方式测试 splice
console.log(y.splice(2,5));//2,3,4,5,6
console.log(y);//[0,1,7,8,9]显示原数组中的数值被剔除掉了
如何在 JS 中动态添加 / 删除对象的属性?
咱们可以使用 object.property_name = value
向对象添加属性,delete object.property_name
用于删除属性。
例如:
let user = new Object();
// adding a property
user.name='Anil';
user.age =25;
console.log(user);
delete user.age;
console.log(user);
解释一下什么是 promise?
promise
是 js 中的一个对象,用于生成可能在将来产生结果的值。值可以是已解析的值,也可以是说明为什么未解析该值的原因。
promise 可以有三种状态:
- pending:初始状态,既不是成功也不是失败
- fulfilled:意味着操作完全成功
- rejected:意味着操作失败
一个等待状态的 promise 对象能够成功后返回一个值,也能失败后带回一个错误 当这两种情况发生的时候,处理函数会排队执行通过 then 方法会被调用
数组去重复的方法有哪些
1. 使用set
function uniquearray(array) {let unique_array= Array.from(set(array)) return unique_array; }
2. 使用filter
function unque_array (arr) {let unique_array = arr.filter(function(elem, index, self) {return index == self.indexOf(elem);
})
return unique_array;
}
console.log(unique_array(array_with_duplicates));
3. 使用 for
循环
Array dups_names = ['Ron', 'Pal', 'Fred', 'Rongo', 'Ron'];
function dups_array(dups_names) {let unique = {};
names.forEach(function(i) {If (!unique[i]) {unique[i] = true; }
});
return Object.keys(unique);} // Ron, Pal, Fred, Rongo
Dups_array(names);
undefined,null 和 undeclared 有什么区别?
1.null 表示 ” 没有对象 ”,即该处不应该有值,转为数值时为 0。典型用法是:
(1)作为函数的参数,表示该函数的参数不是对象。
(2)作为对象原型链的终点。
2.undefined 表示 ” 缺少值 ”,就是此处应该有一个值,但是还没有定义,转为数值时为 NaN。典型用法是:
(1)变量被声明了,但没有赋值时,就等于 undefined。
(2) 调用函数时,应该提供的参数没有提供,该参数等于 undefined。
(3)对象没有赋值的属性,该属性的值为 undefined。
(4)函数没有返回值时,默认返回 undefined。
3.undeclared:js 语法错误,没有申明直接使用,js 无法找到对应的上下文。
JS 中 == 和 === 区别是什么?
1、对于 string
,number
等基础类型,==
和 ===
有区别
1)不同类型间比较,==
之比较“转化成同一类型后的值”看“值”是否相等,===
如果类型不同,其结果就是不等。2)同类型比较,直接进行“值”比较,两者结果一样。
2、对于 Array
,Object
等高级类型,==
和 ===
没有区别
进行“指针地址”比较。
3、基础类型与高级类型,==
和 ===
有区别
1)对于==
,将高级转化为基础类型,进行“值”比较。
2)因为类型不同,===
结果为false
。
解释 JS 中的事件冒泡和事件捕获
事件捕获和冒泡: 在 HTML DOM API 中,有两种事件传播方法,它们决定了接收事件的顺序。两种方法是事件冒泡和事件捕获。第一个方法事件冒泡将事件指向其预期的目标,第二个方法称为事件捕获,其中事件向下到达元素。
事件捕获
捕获过程很少被使用,但是当它被使用时,它被证明是非常有用的。这个过程也称为 滴流模式
。在这个过程中,事件首先由最外层的元素捕获,然后传播到最内部的元素。例如:
<div>
<ul>
<li></li>
</ul>
</div>
从上面的示例中,假设单击事件发生在 li
元素中,在这种情况下,捕获事件将首先处理div
,然后处理ul
,最后命中目标元素li
。
事件冒泡
冒泡的工作原理与冒泡类似,事件由最内部的元素处理,然后传播到外部元素。
<div>
<ul>
<li></li>
</ul>
</div>
从上面的例子中,假设 click
事件确实发生在冒泡模型中的 li
元素中,该事件将首先由 li
处理,然后由 ul
处理,最后由 div
元素处理。
module.exports 和 exports 之间有什么区别?
module
和 exports
是Node.js
给每个 js
文件内置的两个对象。可以通过 console.log(module)
和console.log(exports)
打印出来。如果你在 main.js
中写入下面两行,然后运行$ node main.js
:
console.log(exports);// 输出:{}
console.log(module);// 输出:Module {..., exports: {}, ...}(注:... 代表省略了其他一些属性)
从打印咱们可以看出,module.exports
和 exports
一开始都是一个空对象 {}
,实际上,这两个对象指向同一块内存。这也就是说module.exports
和exports
是等价的(有个前提:不去改变它们指向的内存地址)。
例如:exports.age = 18
和 module.export.age = 18
,这两种写法是一致的(都相当于给最初的空对象{}
添加了一个属性,通过 require
得到的就是{age: 18}
)
import 和 exports 是什么?
import
和 exports
帮助咱们编写模块化的 JS 代码。使用 import
和exports
,咱们可以将代码分割成多个文件。import
只允许获取文件的某些特定变量或方法。可以导入模块导出的方法或变量。
//index.js
import name,age from './person';
console.log(name);
console.log(age);
//person.js
let name ='Sharad', occupation='developer', age =26;
export {name, age};
如何在 JS 中克隆对象
Object.assign()
方法用于在 JS 中克隆对象。如:
var x = {myProp: "value"};
var y = Object.assign({}, x);
如何在 JS 中编码和解码 URL?
var uri = "my profile.php?name=sammer&occupation=pāntiNG";
var encoded_uri = encodeURI(uri);
decodeURI(encoded_uri);
BOM 和 DOM 的关系
BOM全称Browser Object Model
,即浏览器对象模型,主要处理浏览器窗口和框架。
DOM
全称Document Object Model
,即文档对象模型,是 HTML 和 XML 的应用程序接口(API),遵循 W3C 的标准,所有浏览器公共遵守的标准。
JS 是通过访问 BOM(Browser Object Model)对象来访问、控制、修改客户端(浏览器),由于BOM 的window
包含了 document
,window
对象的属性和方法是直接可以使用而且被感知的,因此可以直接使用 window
对象的 document
属性,通过 document 属性就可以访问、检索、修改 XHTML 文档内容与结构。因为 document
对象又是 DOM
的根节点。
可以说,BOM
包含了 DOM
(对象),浏览器提供出来给予访问的是BOM
对象,从 BOM
对象再访问到 DOM
对象,从而 js 可以操作浏览器以及浏览器读取到的文档。
列出一些内置方法及其返回的值
内置方法返回值 CharAt()它返回指定索引处的字符。Concat()它连接两个或多个字符串。forEach()它为数组中的每个元素调用一个函数。indexOf()它返回指定值第一次出现时调用字符串对象中的索引。length()它返回字符串的长度。pop()它从数组中删除最后一个元素并返回该元素。push()它将一个或多个元素添加到数组的末尾,并返回数组的新长度。reverse()反转数组元素的顺序。
null 和 undefined 区别
undefined
是基本数据类型 表示未定义 缺少的意思。
null
是引用数据类型,是对象,表示空对象
undefined
是从 null
派生出来的 所以 undefined==null
为true
undeclared 和 undefined 区别?
undeclared 的变量是程序中不存在且未声明的变量。如果程序尝试读取未声明变量的值,则会遇到运行时错误。undefined 的变量是在程序中声明但未赋予任何值的变量,如果程序试图读取未定义变量的值,则返回 undefined 的值。
call 和 apply 有什么区别
call
和 apply
可以用来重新定义函数的执行环境,也就是 this
的指向;call
和 apply
都是为了改变某个函数运行时的 context
,即上下文而存在的,换句话说,就是为了改变函数体内部this
的指向。
call()
调用一个对象的方法,用另一个对象替换当前对象,可以继承另外一个对象的属性,它的语法是:
Function.call(obj[, param1[, param2[, [,...paramN]]]]);
说明:call
方法可以用来代替另一个对象调用一个方法,call
方法可以将一个函数的对象上下文从初始的上下文改变为 obj
指定的新对象,如果没有提供 obj
参数,那么 Global
对象被用于obj
apply()
和 call()
方法一样,只是参数列表不同,语法:
Function.apply(obj[, argArray]);
说明 :如果argArray
不是一个有效数组或不是 arguments
对象,那么将导致一个 TypeError
,如果没有提供argArray
和obj
任何一个参数,那么 Global
对象将用作 obj。
如何在 JS 中清空数组
有许多方法可以用来清空数组:
方法一:
arrayList = []
上面的代码将把变量 arrayList
设置为一个新的空数组。如果在其他任何地方都没有对原始数组 arrayList
的引用,则建议这样做,因为它实际上会创建一个新的空数组。咱们应该小心使用这种清空数组的方法,因为如果你从另一个变量引用了这个数组,那么原始的引用数组将保持不变。
方法二:
arrayList.length = 0;
上面的代码将通过将其 length
设置为 0
来清除现有数组。这种清空数组的方式还会更新指向原始数组的所有引用变量。因此,当你想要更新指向 arrayList
的所有引用变量时,此方法很有用。
方法三:
arrayList.splice(0, arrayList.length);
这处方法也行,当然这种清空数组的方法也将更新对原始数组的所有引用。
方法四:
while(arrayList.length)
{arrayList.pop();
}
上面的实现也可以空数组,但通常不建议经常使用这种方式。
new 操作符具体干了什么呢 ?
- 创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型。
- 属性和方法被加入到 this 引用的对象中。
- 新创建的对象由 this 所引用,并且最后隐式的返回 this。
把伪数组转换为数组
只需使用 [].slice.call(elements) 即可实现:
var elements = document.querySelectorAll("p"); // NodeList
var arrayElements = [].slice.call(elements); // 现在 NodeList 是一个数组
var arrayElements = Array.from(elements); // 这是另一种转换 NodeList 到 Array 的方法
JavaScript 判断一个变量是对象还是数组?
typeof 都返回 object
在 JavaScript 中所有数据类型严格意义上都是对象,但实际使用中我们还是有类型之分,如果要判断一个变量是数组还是对象使用 typeof 搞不定,因为它全都返回 object。
第一,使用 typeof 加 length 属性
数组有 length 属性,object 没有,而 typeof 数组与对象都返回 object,所以我们可以这么判断
var getDataType = function(o){if(typeof o == 'object'){if( typeof o.length == 'number'){return 'Array';} else {return 'Object';}
} else {return 'param is no object type';}
};
第二,使用 instanceof
利用 instanceof 判断数据类型是对象还是数组时应该优先判断 array,最后判断 object。
var getDataType = function(o){if(o instanceof Array){return 'Array'} else if (o instanceof Object){return 'Object';} else {return 'param is no object type';}
};
如何将一个数组打乱
方法 1:arr.sort(() => Math.random() - 0.5)
;
sort 方法使用了插入排序(目标长度小于 10)和快排,元素之间的比较远小于 n(n-1)/2,因此有些元素间没有随机交换的可能,使得该方法不够随机。
方法 2:Fisher-Yates Shuffle,复杂度为 O(n)。从后向前遍历,不断将当前元素与随机位置的元素(除去已遍历的部分)进行交换。
function shuffle(arr) {
let m = arr.length;
while (m > 1){let index = Math.floor(Math.random() * m--);
[arr[m] , arr[index]] = [arr[index] , arr[m]]
}
return arr;
}
var list = [1, 2, 3];
console.log(list.sort(function() {Math.random() - 0.5 })); // [2, 1, 3]
MVVM
MVVM 是 Model-View-ViewModel 的缩写,Model 代表数据模型,View 代表用户操作界面,ViewModel 则是视图数据层。ViewModel 通过双向数据绑定将 View 和 Model 层连接了起来,开发人员不用手动操作 Dom 元素,View 和 Mode 会自动双向同步,开发者只需要关注业务逻辑即可。
图片懒加载
先将 img 标签的 src 链接设为同一张图片(比如某空白图片),然后给 img 标签设置自定义属性(比如 data-src),并将真正的图片地址存储在其中。当 js 监听到该图片元素进入可视窗口时(获取 img 节点距离浏览器顶部的距离,如果小于或等于浏览器窗口的可视高度即进入),再将自定义属性中的地址存储到 src 属性中,达到懒加载的效果。这样不仅可以减轻服务器压力,也可以提高用户体验。
把 Script 放 body 前后有什么区别?浏览器会如何解析它们?
html 标签只包含 head 和 body 两个标签,解析时,所有标签都会解析进这两个标签里边。body 之前的任何位置都会解析进 head 里边,之后的都会解析进 body 里边。
js 延迟加载的方式有哪些?
共有:defer 和 async、动态创建 DOM 方式(用得最多)、使用 jQuery 的 getscript 方法、使用 settimeout 延迟方法、让 js 最后加载
defer 属性:(页面 load 后执行)
HTML 4.01 为 <script>
标签定义了 defer
属性。
用途:表明脚本在执行时不会影响页面的构造。也就是说,脚本会被延迟到整个页面都解析完毕之后再执行。
在 <script>
元素中设置 defer
属性,等于告诉浏览器立即下载,但 延迟执行。
<html>
<head>
<script src="test1.js" defer="defer"></script>
<script src="test2.js" defer="defer"></script>
</head>
<body>
<!-- 这里放内容 -->
</body>
</html>
说明:虽然 <script> 元素放在了 <head> 元素中,但包含的脚本将延迟浏览器遇到 </html> 标签后再执行。
HTML5 规范要求脚本按照它们出现的先后顺序执行。在现实当中,延迟脚本并不一定会按照顺序执行。
defer 属性只适用于外部脚本文件。支持 HTML5 的实现会忽略嵌入脚本设置的 defer 属性。
- async 属性
HTML5 为 <script> 标签定义了 async 属性。与 defer 属性类似,都用于改变处理脚本的行为。同样,只适用于外部脚本文件。
目的:不让页面等待脚本下载和执行,从而异步加载页面其他内容。
异步脚本一定会在页面 load 事件前执行。
不能保证脚本会按顺序执行。
<!DOCTYPE html>
<html>
<head>
<script src="test1.js" async></script>
<script src="test2.js" async></script>
</head>
<body>
<!-- 这里放内容 -->
</body>
</html>
async 和 defer 一样,都不会阻塞其他资源下载,所以不会影响页面的加载。
缺点:不能控制加载的顺序
3. 动态创建 DOM 方式
// 这些代码应被放置在 </body> 标签前(接近 HTML 文件底部)
<script type="text/javascript">
function downloadJSAtOnload() {varelement = document.createElement("script");
element.src = "defer.js";
document.body.appendChild(element);
}
if (window.addEventListener)
window.addEventListener("load",downloadJSAtOnload, false);
else if (window.attachEvent)
window.attachEvent("onload",downloadJSAtOnload);
else
window.onload =downloadJSAtOnload;
</script>
4. 使用 jQuery 的 getScript()方法
$.getScript("outer.js",function(){// 回调函数,成功获取文件后执行的函数
console.log("脚本加载完成")
});
5. 使用 setTimeout 延迟方法
6. 让 JS 最后加载
把 js 外部引入的文件放到页面底部,来让 js 最后引入,从而加快页面加载速度