new一个对象,这个过程中发生了什么?

var obj = new Object("name","sansan");
  1. 创建一个新对象,如:var obj = {};
  2. 新对象的_proto_属性指向构造函数的原型对象。
  3. 将构造函数的作用域赋值给新对象。(也所以this对象指向新对象)
  4. 执行构造函数内部的代码,将属性添加给obj中的this对象。
  5. 返回新对象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 则是立即执行

异步过程的构成要素有哪些?和异步过程是怎样的 ?

总结一下,一个异步过程通常是这样的:

  • 主线程发起一个异步请求,相应的工作线程接收请求并告知主线程已收到(异步函数返回);
  • 主线程可以继续执行后面的代码,同时工作线程执行异步任务;
  • 工作线程完成工作后,通知主线程;
  • 主线程收到通知后,执行一定的动作(调用回调函数)。
  1. 异步函数通常具有以下的形式:A(args..., callbackFn)。
  2. 它可以叫做异步过程的发起函数,或者叫做异步任务注册函数。
  3. 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中如何将页面重定向到另一个页面?

  1. 使用 window.location.href =“https://www.onlineinterviewquestions.com/”
  2. 使用 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.使用setfunction 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 之间有什么区别?

moduleexportsNode.js给每个js文件内置的两个对象。可以通过console.log(module)console.log(exports)打印出来。如果你在main.js中写入下面两行,然后运行$ node main.js:

console.log(exports);//输出:{}
console.log(module);//输出:Module {..., exports: {}, ...} (注:...代表省略了其他一些属性)

从打印咱们可以看出,module.exportsexports一开始都是一个空对象{},实际上,这两个对象指向同一块内存。这也就是说module.exportsexports是等价的(有个前提:不去改变它们指向的内存地址)。

例如:exports.age = 18module.export.age = 18,这两种写法是一致的(都相当于给最初的空对象{}添加了一个属性,通过require得到的就是{age: 18}

import 和 exports 是什么?

importexports帮助咱们编写模块化的JS代码。使用importexports,咱们可以将代码分割成多个文件。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)对象来访问、控制、修改客户端(浏览器),由于BOMwindow包含了documentwindow对象的属性和方法是直接可以使用而且被感知的,因此可以直接使用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==nulltrue

undeclared 和 undefined 区别?

undeclared的变量是程序中不存在且未声明的变量。 如果程序尝试读取未声明变量的值,则会遇到运行时错误。undefined的变量是在程序中声明但未赋予任何值的变量,如果程序试图读取未定义变量的值,则返回undefined的值。

call 和 apply有什么区别

callapply可以用来重新定义函数的执行环境,也就是this的指向;callapply都是为了改变某个函数运行时的context,即上下文而存在的,换句话说,就是为了改变函数体内部this的指向。

call()调用一个对象的方法,用另一个对象替换当前对象,可以继承另外一个对象的属性,它的语法是:

Function.call(obj[, param1[, param2[, [,...paramN]]]]);

说明:call方法可以用来代替另一个对象调用一个方法,call方法可以将一个函数的对象上下文从初始的上下文改变为obj指定的新对象,如果没有提供obj参数,那么Global对象被用于obj

apply()call()方法一样,只是参数列表不同,语法:

Function.apply(obj[, argArray]);

说明:如果argArray不是一个有效数组或不是arguments对象,那么将导致一个TypeError,如果没有提供argArrayobj任何一个参数,那么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属性。

  1. 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最后引入,从而加快页面加载速度


前端工程师
4 声望0 粉丝