文章目录
js的数据类型
基本数据类型(7个):number、string、boolean、null、undefined、symbol、bigInt
引用数据类型(2个):Array、Object
如何判断简单数据类型(typeof)
typeof "123" => "string"
typdof 123 => "number"
typeof true => "boolean"
typeof null => "object"
typeof undefined => "undefined"
`可以看出 typeof 对于引用数据类型不好用`
typeof [1,2,3] => "object"
typeof {name:'zs'} => "object"
typeof 函数 => "function"
如何判断引用数据类型
1.Array.isArray() 判断该实例对象是不是数组
2.a instanceof b 判断该a实例对象是不是属于b这个构造函数
3.a.constructor 判断a的构造函数是什么
var a = [1,2,3];
var b = {
name:'zs'
}
- Array.isArray()
console.log( Array.isArray(a))//true
console.log( Array.isArray(b))//false
- instanceof(原理)
原理:
A instanceof B
instanceof用于检测对象A是不是对象B的实例,而检测是基于原型链进行查找的,也就是说对象B的prototype有没有在对象A的__proto__原型链上,如果有就返回true,,否则返回false
console.log(a instanceof Array);//true
console.log(b instanceof Object);//true
- constructor
console.log(a.constructor == Array);//true
console.log(b.constructor == Object);//true
原型链
什么是原型链
每个对象拥有一个原型对象,通过 proto 指针指向其原型对象,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层,最终指向 null(Object.proptotype.__proto__指向的是null)。这种关系被称为原型链(prototype chain),通过原型链一个对象可以拥有定义在其他对象中的属性和方法
构造函数有一个prototype属性,这个属性是一个指针 指向他的原型对象
原型对象下的属性和方法 可以被实例化对象所共享
原型对线下有一个constructor 属性指向他的构造函数
实例化对象下边有个__proto__属性指向构造函数的原型对象
继承(es6之前)
父类Person
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.say = function(){
console.log(this.name +"haha");
}
子类Coder
function Coder(name,age){
// this
// call方法可以改变this指向
Person.call(this,name,age);
}
// 继承父类的方法
// 不可以Coder.prototype = Person.prototype; 因为这样后边在更改constructor属性指向时会把Person.prototype.constructor一起更改
Coder.prototype = new Person();
// 此时Coder的原型对象下的constructor属性 指向Person 所以要再给他指回Coder
Coder.prototype.constructor = Coder;
this指向问题(this指向谁是在函数执行的时候决定的,而不是在定义时候决定的)
this一般指向函数的调用者
调用方法 | this指向 |
---|---|
普通函数 | window |
定时器(不是箭头函数) | window |
立即执行函数(不是箭头函数) | window |
构造函数 | 实例化对象 |
对象方法的调用 | 该方法所属对象 |
事件绑定(不要用箭头函数) | 绑定的事件对象 |
怎么改变this指向(call、apply、bind)
在函数后边调用这三个方法
- call(thisArg,a,b,c)
- apply(thisArg,[a,b,c])
- bind(thisArg,a,b,c)
其中:thisArg代表你想要this指向的东西
a b c代表函数要的参数
call、apply、bind的不同
- call和apply会直接调用函数,使函数执行。而bind不会直接执行函数,只会改变函数的this指向
- call和bind的传参都是a,b,c。而apply传递参数则是需要用数组[a,b,c]
手写bind call apply
事件源和事件处理函数中this的区别
事件源是指触发了事件处理函数的源头
而this指向的是绑定了处理函数的对象
乍一听二者可能没啥区别!那么请看下边代码
<div id="div1">
div1
<div id="div2">
div2
</div>
</div>
#div1{
width: 200px;
height: 200px;
background: #ff0000;
}
#div2{
width: 100px;
height: 100px;
background: #0000ff;
}
var oDiv = document.getElementById('div1');
oDiv.onclick = function(e){
console.log('哈哈哈哈');
// 事件源
console.log(e.target);
// this 对当前对象引用
console.log(this);
}
此时我们点击div1得到如下输出
我们再点击一下div2,由于事件冒泡
点击div2同样会触发div1的点击事件。得到如下输出
结论
事件源(e.target)指的是触发了这个事件的罪魁祸首
this指的是事件函数所绑定的那个对象
事件冒泡和事件捕获
事件冒泡
就和水壶烧水一样,气泡是从内部像外部移动的。类比到事件冒泡就是点击内部子元素除了会触发自己本身的点击事件同时也会逐级触发祖先元素的点击事件。
事件捕获
就和俄罗斯套娃一样,你想摸最里边的,就得依次经历外边基层的套娃。点击子元素会先触发最外层的父元素然后逐级向里触发子元素
当事件冒泡与事件捕获同时存在
它会先进行事件捕获,然后再进行事件冒泡
代码与结果如下:
<div id="div1">
div1
<div id="div2">
div2
</div>
</div>
#div1 {
width: 200px;
height: 200px;
background: #ff0000;
}
#div2 {
width: 100px;
height: 100px;
background: #0000ff;
}
var oDiv1 = document.getElementById('div1');
var oDiv2 = document.getElementById('div2');
// 同时有冒泡和捕获的时候 先捕获 再冒泡 (先从外到里,再从里往外)
oDiv1.addEventListener('click', function () {
console.log('div1 捕获');
}, true);
oDiv2.addEventListener('click', function () {
console.log('div2 捕获');
}, true);
oDiv1.addEventListener('click', function () {
console.log('div1 冒泡');
}, false);
oDiv2.addEventListener('click', function () {
console.log('div2 冒泡');
}, false);
点击div2后
事件委托
通过事件冒泡和事件源的配合,来解决后生成元素的事件绑定问题
例子如下所示
下面的代码for循环只给前三个li进行了点击事件的绑定,而通过button点击生成的新li并没有点击事件
<button id="btn">click</button>
<ul id="ul1">
<li>11111</li>
<li>22222</li>
<li>33333</li>
</ul>
li{
background: #ff0000;
}
var aLi = document.getElementsByTagName('li');
var oBtn = document.getElementById('btn');
var oUl = document.getElementById('ul1');
for(var i=0; i<aLi.length; i++){
aLi[i].onclick = function(){
console.log(this.innerHTML);
}
}
oBtn.onclick = function(){
var oLi = document.createElement('li');
oLi.innerHTML = Math.random();
oUl.appendChild(oLi);
}
通过事件委托改造后的代码,可以实现新生成的li也有点击事件
<button id="btn">click</button>
<ul id="ul1">
<li>11111</li>
<li>22222</li>
<li>33333</li>
</ul>
li{
background: #ff0000;
}
var aLi = document.getElementsByTagName('li');
var oBtn = document.getElementById('btn');
var oUl = document.getElementById('ul1');
oBtn.onclick = function(){
var oLi = document.createElement('li');
oLi.innerHTML = Math.random();
oUl.appendChild(oLi);
}
oUl.onclick = function(e){
// console.log(e.target.tagName)
if(e.target.tagName == "LI"){
console.log(e.target.innerHTML);
}
}
阻止事件冒泡以及默认行为
event.stopPropagation()//阻止事件冒泡
event.preventDefault()//阻止默认行为
闭包
内部函数可以访问外部函数的变量(也包括外部函数的参数)
作用:
- 避免了对全局变量的污染
- 可以是一个变量长期驻扎在内存之中
缺点:
容易发生内存泄漏
例子
function fn1(val){
// 这个a会一直保存在内存中因为你也不知道啥时候还回调用内部返回的这个function
var a = 20;
return function(){
a++;
console.log(a,val);
}
}
var f = fn1();
f();
f();
f();
<ul>
<li>1111</li>
<li>222</li>
<li>3333</li>
</ul>
var aLi = document.getElementsByTagName('li');
for(var i=0; i<aLi.length; i++){
(function(i){
// i是外部函数中的参数
aLi[i].onclick = function(){
console.log(i);
}
})(i);
}
节流
如果某个函数需要频繁触发,出于性能优化考虑,在规定时间内只让函数的第一次生效
例子
// 闭包 this
function throttle(fn,delay){
var startTime = 0;
// 这个return的函数this指向的是事件绑定的那个对象
return function(){
var nowTime = Date.now();
if(nowTime - startTime > delay){
fn.call(this);
startTime = nowTime;
}
}
}
document.onmousemove = throttle(function(){
console.log(Date.now())
console.log(this);
},1000);
防抖
一个需要频繁触发的函数,在规定时间内只让最后一次生效,前面的都不生效
例子
<button id="btn">点击</button>
<script>
var btn = document.querySelector('#btn')
// 用闭包技术存储timer,防止污染全局变量
function fangdou(fn, delay) {
var timer = null
return function () {
clearTimeout(timer)
timer = setTimeout(() => {
// 因为fn是一个普通函数所以他的this指向的是window,为了让他的this指向绑定事件的对象便用call来改变this指向
fn.call(this)
}, delay)
}
}
btn.onclick = fangdou(function () {
console.log(this)
}, 300)
</script>
同步任务与异步任务(js是单线程的)
同步任务
在主线程上排队执行的任务,只有前一任务执行完后面的任务才可以依次执行。
异步任务
不进入主线程,而是进入任务队列的任务,只有当主线程全部执行完了才会去看任务队列中是否有异步任务可以开始执行了。如果有则把该异步任务拉到主线程进行执行
事件循环
- 在所有同步任务都在主线程上执行,形成一个
执行栈
- 主线程之外,还存在一个任务队列。只要异步任务有了运行结果,就在任务队列之中放置一个事件
- 一旦
执行栈
中所有的同步任务都执行完毕,系统就会读取任务队列,看看里边有哪些事件。那些对应的异步任务,就会进入执行栈,开始执行。 - 主线程不断重复第三个步骤
任务队列分为:宏任务和微任务
- 宏任务:包括整体代码
script
,setTimeout
,setInterval
,setImmediate
- 宏任务所处的队列称为
宏任务队列
- 宏任务队列可以有多个
- 宏任务所处的队列称为
- 微任务:包括
new Promise.then(回调)
,process.nextTick
- 微任务所处的队列称为
微任务队列
- 只有一个微任务队列
- 微任务所处的队列称为
总结:执行栈会先看微任务队列有没有任务,然后才会去看宏任务队列。
注意
:其实在微任务执行完不会立马执行宏任务而是会DOM渲染,然后才是宏任务
例子
console.log('1');
setTimeout(function() { //1-宏任务
console.log('2');
process.nextTick(function() { //2-1微任务
console.log('3');
});
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() { //2-2微任务
console.log('5')
});
})
process.nextTick(function() { //1-微任务
console.log('6');
})
// 1 6 2 4 3 5
结果:
AJAX原理
存有 XMLHttpRequest 的状态(就是下边代码的readyState属性)。从 0 到 4 发生变化。
0: 请求未初始化
1: 服务器连接已建立
2: 请求已接收
3: 请求处理中
4: 请求已完成,且响应已就绪
status(就是下边代码的status属性)
200 (“OK”) 成功 一切正常
500 服务器方面的问题
503 (服务不可用)
404 找不到资源
301 永久重定向
302 临时重定向
304 缓存
GET请求示例
//创建 XMLHttpRequest 对象
var xmlhttp;
if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp = new XMLHttpRequest();
} else { // code for IE6, IE5
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
// 向服务器发送GET请求 请求方式 url 是否异步
// get请求时候参数写在接口地址后边就行
xmlhttp.open("GET", "test1.txt?name=zs", true);
xmlhttp.send();
// 服务器响应 异步的
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
console.log(xmlhttp.responseText);
}
}
POST请求示例
//创建 XMLHttpRequest 对象
var xmlhttp;
if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp = new XMLHttpRequest();
} else { // code for IE6, IE5
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
// post 请求方式 url 是否异步
xmlhttp.open("POST","ajax_test.asp",true);
// 设置请求头 "Content-type","application/x-www-form-urlencoded"说明post传给后台的数据是表单(form)数据
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
// post请求时候参数写在send中
xmlhttp.send("fname=Bill&lname=Gates");
// 服务器响应 异步的
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
console.log(xmlhttp.responseText);
}
}
es6新特性
let var const区别***
let、const同var的区别
- let和const没有变量提升
- let和const不可以重复声明同一个变量
- let和const具有块级作用域
- let和const有暂时性死区
例子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
let a = 10
function b() {
console.log(a);
let a = 15
console.log(a);
}
b()
</script>
</body>
</html>
这是因为let声明的变量在当前作用域下不允许同名的变量进来,又因为let不可以变量提升,所以函数中的第一次console.log(a)报错---------这就是所谓的暂时性死区
const独有的特性
const是定义常量的,如果是基本数据类型 就是不能变的,如果是对象的话,对象的存储地址(指针)是不能变的,属性值是可以变的,如果想让对象的值不能变的就
Object.freeze(obj);
这样值也是不能变的了
箭头函数
普通函数和箭头函数this指向上的区别:
(1)this指向问题
- 普通函数this是在调用的时候才能确定this指向
- 箭头函数this是在声明时候就已经确定了this指向,指向的是父作用域的this
(2)箭头函数不能用来当构造函数(也就是不能new)
(3)箭头函数不能使用arguments 可以使用…rest(这个叫做剩余参数
)来替代
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
var a = (k, ...rest) => {
console.log('k', k);
console.log('rest', rest);
}
a(1, 2, 1, 3, 4, 5, 3, 1)
function b() {
console.log(arguments);
}
b(1, 2, 3, 1, 23, 2)
</script>
</body>
</html>
箭头函数与普通函数的区别
- this指向不同
- call、apply、bind等方法不能改变箭头函数的this指向
- 箭头函数不可以作为构造函数
- 箭头函数没有自己的arguments.
- 箭头函数没有prototype
解构赋值
数组解构
函数解构
扩展运算符 …
把数组或者类数组展开成用逗号隔开的值
- 数组复制
const a1 = [1, 2];
const a2 = [...a1];
- 合并数组
let a1 = [1,2]
let a2 = [3,4]
let a3 = [1,4]
let a = [...a1, ...a2, ...a3] // [1, 2, 3, 4, 1, 4]
- 把类数组转化成数组
- 和解构赋值结合生成数组(扩展运算符参数必须在最后一位)
let [a1, ...list] = [1, 2, 3, 4, 5];
console.log(list) // [2, 3, 4, 5]
- 和set结构结合实现数组或字符串去重
let a1 = [1, 2, 3, 4, 1, 4];
// 数组去重
console.log([...new Set(a1)]) // [1, 2, 3, 4]
//字符串去重
console.log([...new Set('ababbc')].join('')) // abc
- 和解构赋值结合生成对象(扩展运算符参数必须在最后一位)
let { a, b, ...z } = { a: 1, b: 2, c: 3, d: 4 };
console.log(a) // 1
console.log(b) // 2
console.log(z) // { c: 3, d: 4 }
- 合并对象(多个对象有同名属性,则后面的会覆盖前面的)
let a1 = { a: 1, b: 2 };
let a2 = { a: 3, c: 4 };
let a3 = { ...a1, ...a2} // {a: 3, b: 2, c: 4} 可以发现a:1被覆盖掉了
Promise
异步编程的一种解决方案
resolve,reject,then,catch的用法
一个promise对象有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)
一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。
当你在new Promise()中调用了resolve()这时promise对象的状态pending变为fulfilled
当你在new Promise()中调用了reject()这时promise对象的状态pending变为rejected
当promise对象的状态为fulfilled时候,便会执行到.then()里边的函数,相反的当promise对象的状态为rejected时候,便会执行到.catch()里边的函数或者.then()里边的第二个函数
注意1
:then()和catch()就当做回调函数看待,他们前边的promise对象无论执行的多快,then和catch都会被放到执行队列中,等待执行栈全部执行完才会执行他俩。
注意2
:.then()和.catch()返回的也是一个promise对象
then和catch里边正常返回的是fulfilled的promise对象,例如:return 100
then和catch里边如果有报错就返回rejected的promise对象, 例如:throw new Error(‘the error’)
例子:
function promiseClick() {
let p = new Promise(function (resolve, reject) {
setTimeout(function () {
var num = Math.ceil(Math.random() * 20); //生成1-20的随机数
console.log('随机数生成的值:', num)
if (num <= 10) {
resolve(num);
}
else {
reject('数字太于10了即将执行失败回调');
}
}, 2000);
})
return p
}
promiseClick().then(
function (data) {
console.log('resolved成功回调');
console.log('成功回调接受的值:', data);
},
function (reason) {
console.log('rejected失败回调');
console.log('失败执行回调抛出失败原因:', reason);
}
);
// 这个里边的catch就和上边那个then里的第二个函数一个作用
promiseClick().then(
function (data) {
console.log('resolved成功回调');
console.log('成功回调接受的值:', data);
}
).catch(
function (reason) {
console.log('rejected失败回调');
console.log('失败执行回调抛出失败原因:', reason);
}
)
all和race的用法
all
只有当所有的promise对象状态都是fulfilled时候,all才会返回一个fulfilled的promise对象,否则返回的都是rejected的promise对象
例子1
function promiseClick1() {
let p = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve(1)
}, 1000)
})
return p
}
function promiseClick2() {
let p = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve(12)
}, 2000)
})
return p
}
function promiseClick3() {
let p = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve(123)
}, 3000)
})
return p
}
Promise.all([promiseClick1(), promiseClick2(), promiseClick3()]).then(data => {
console.log('三个promise对象状态都是fulfilled');
console.log(data);
})
例子2
function promiseClick1() {
let p = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve(1)
}, 1000)
})
return p
}
function promiseClick2() {
let p = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve(12)
}, 2000)
})
return p
}
function promiseClick3() {
let p = new Promise(function (resolve, reject) {
setTimeout(() => {
reject(123)
}, 3000)
})
return p
}
Promise.all([promiseClick1(), promiseClick2(), promiseClick3()]).then(data => {
console.log('三个promise对象状态都是fulfilled');
console.log(data);
}).catch((res) => {
console.log('三个promise对象肯定有状态不是fulfilled');
console.log(res);
})
race
race是只要有一个promise对象完事了,就按照这个promise对象的状态返回一个promise对象。其余的promise对象一概不理会
例子1
function promiseClick1() {
let p = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve(1)
}, 1000)
})
return p
}
function promiseClick2() {
let p = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve(12)
}, 2000)
})
return p
}
function promiseClick3() {
let p = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve(123)
}, 3000)
})
return p
}
Promise.race([promiseClick1(), promiseClick2(), promiseClick3()]).then(data => {
console.log('fulfilled', data);
}).catch((res) => {
console.log('rejected', res)
})
例子2
function promiseClick1() {
let p = new Promise(function (resolve, reject) {
setTimeout(() => {
// 这里和例子1不同
reject(1)
}, 1000)
})
return p
}
function promiseClick2() {
let p = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve(12)
}, 2000)
})
return p
}
function promiseClick3() {
let p = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve(123)
}, 3000)
})
return p
}
Promise.race([promiseClick1(), promiseClick2(), promiseClick3()]).then(data => {
console.log('fulfilled', data);
}).catch((res) => {
console.log('rejected', res)
})
async和await
async
1.async定义的函数是异步函数,也就是说它不会阻塞其后边代码的执行。
2.async定义的函数返回的是一个promise对象。当你return一个值的时候他就会返回一个状态为fulfilled的promise对象,当你throw一个值的时候他就会返回一个状态为rejected的promise对象。
async function a(flag) {
if (flag) {
return '我是真滴帅'
} else {
throw '报错了'
}
}
console.log(a(true))
console.log(a(false));
await
await只能放在async函数中,作用是让后面的执行语句或方法要等待当前await方法的结果后才能再执行。
function doubleAfter2seconds(num) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2 * num)
}, 2000);
})
}
async function testResult() {
let first = await doubleAfter2seconds(30);
let second = await doubleAfter2seconds(50);
let third = await doubleAfter2seconds(30);
console.log(first + second + third);
}
testResult()
6s后输出220
总结
- async和await基本是组合使用的,async用来声明一个异步方法,返回的是一个promise对象,如果要获取到对应的返回值,就需要使用.then方法;
- await后边的代码相当于Promise的then方法里的代码,所以它无法捕获异常(也就是状态为rejected的promise)
- 捕获异常可以采用try…catch
ES6的类
定义类
通过class关键字以及constructor函数来定义类
class People {
constructor(name, age) {
this.name = name;
this.age = age
}
say() {
console.log('我叫' + this.name + '今年' + this.age);
}
}
var p1 = new People('张三', 18)
console.log(p1);
p1.say()
静态方法
如Array.isArray()和Object.freeze()这种构造函数
调用的方法称为:静态方法
class People {
constructor(name, age) {
this.name = name;
this.age = age
}
say() {
console.log('我叫' + this.name + '今年' + this.age);
}
// 这个就是静态函数
static k() {
console.log('我是静态函数');
}
}
var p1 = new People('张三', 18)
console.log(p1);
p1.say()
// 调用静态函数
People.k()
es6类的继承(上面有es5的继承方法记得回去看看)
通过extends和super来继承父类的属性和方法
class people {
constructor(name, age) {
this.name = name;
this.age = age
}
say() {
console.log('我叫' + this.name + '今年' + this.age);
}
// 这个就是静态函数
static k() {
console.log('我是静态函数');
}
}
var p1 = new people('张三', 18)
console.log(p1);
p1.say()
// 调用静态函数
people.k()
console.log('---------------------分割线-------------------------');
class Coder extends people {
constructor(name, age, job) {
// 继承父元素的属性
super(name, age)
this.job = job
}
coding() {
console.log(this.name + '爱敲代码');
}
}
var c1 = new Coder('李四', 35, '前端工程师')
console.log(c1);
c1.say()
c1.coding()
Coder.k()
for of只可以用在数组,for in在数组和对象上都可以
深拷贝与浅拷贝以及实现的方法
JSON.parse(JSON.stringify(obj))实现深拷贝的局限
map和weakMap的区别
(参考博文)
- Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
- WeakMap 结构与 Map 结构类似,也是用于生成键值对的集合。但是 WeakMap 只接受对象作为键名( null 除外),不接受其他类型的值作为键名。而且 WeakMap 的键名所指向的对象,不计入垃圾回收机制。
JavaScript脚本延迟加载的方式有哪些
- defer 属性: 给 js 脚本添加 defer 属性,这个属性会让脚本的加载与文档的解析同步解析,然后在文档解析完成后再执行这个脚本文件,这样的话就能使页面的渲染不被阻塞。多个设置了 defer 属性的脚本按规范来说最后是顺序执行的,但是在一些浏览器中可能不是这样。
- async 属性: 给 js 脚本添加 async 属性,这个属性会使脚本异步加载,不会阻塞页面的解析过程,但是当脚本加载完成后立即执行 js 脚本,这个时候如果文档没有解析完成的话同样会阻塞。多个 async 属性的脚本的执行顺序是不可预测的,一般不会按照代码的顺序依次执行。
- 动态创建 DOM 方式: 动态创建 DOM 标签的方式,可以对文档的加载事件进行监听,当文档加载完成后再动态的创建 script 标签来引入 js 脚本。
- 使用 setTimeout 延迟方法: 设置一个定时器来延迟加载js脚本文件
- 让 JS 最后加载: 将 js 脚本放在文档的底部,来使 js 脚本尽可能的在最后来加载执行。
数组的原生方法
- 数组和字符串的转换方法:toString()、toLocalString()、join() 其中 join() 方法可以指定转换为字符串时的分隔符。
- 数组尾部操作的方法 pop() 和 push(),push 方法可以传入多个参数。
- 数组首部操作的方法 shift() 和 unshift() 重排序的方法 reverse() 和 sort(),sort() 方法可以传入一个函数来进行比较,传入前后两个值,如果返回值为正数,则交换两个参数的位置。
--------------------------------------上面的方法会改变原数组 -----------------------------------------------
- 数组连接的方法 concat() ,返回的是拼接好的数组,不影响原数组。
- 数组截取办法 slice(),用于截取数组中的一部分返回,不影响原数组。
- 数组插入方法 splice(),影响原数组查找特定项的索引的方法,indexOf() 和 lastIndexOf() 迭代方法 every()、some()、filter()、map() 和 forEach() 方法
- 数组归并方法 reduce() 和 reduceRight() 方法
DOM的性能优化
为什么DOM要进行性能优化呢?
因为操作DOM消耗的内存资源是非常昂贵的,所以要尽量避免重复操作DOM元素
怎么进行优化
1. 将频繁的DOM操作改为一次性操作
比如往id为list的容器中插入10个li,按照以前的做法就是通过for循环进行十次list.appendchild(li),这样就是在频繁操作DOM
下边我们先建个虚拟的容器frag,这个frag并不是DOM树里的节点所以你在appendchild时候也就不会触发DOM的渲染,等你10个li都加入到frag这个虚拟容器之后,再一次性加到list当中,就相当于只操作了一次DOM
2. 将DOM查询做缓存
BOM的一些相关操作
- navigator浏览器信息
- screen屏幕信息(长度宽度)
- location url的信息 href:整个url、protocol:协议、pathname:路径、search:url传递的参数?id=1
- 浏览器的前景后退