JavaScript进阶部分知识梳理
1、作用域链查找规则
先在自己的执行环境中查找,找到了就停止查找,没有找到就往上一级找,直到找到全局作用域,如果还没找到就会报错
2、代码执行出栈和入栈的过程
浏览器在执行js代码时,默认会有一个全局执行环境在栈中,没有出入栈的概念,碰到函数时,会产生一个函数执行环境,以及函数执行环境内的标识符,会一起进入栈中,这个过程就叫
入栈
,等到所有的JS代码执行完毕之后,函数执行环境会执行出栈并且销毁自己作用域内的标识符,释放内存,先进后出,后进先出,这个过程就叫出栈
3、将伪数组转换为真数组
function fn(){
// 方法一
let arr = [];
for(let i = 0; i < arguments.length; i++){
arr.push(arguments[i])
}
// 方法二
arguments = Array.prototype.slice.call(arguments);
// 方法三
[...arguments]
}
4、箭头函数可以省略的两个地方
1、有且仅有一个形参时可以省略小括号
2、函数执行代码只有一句时可以省略大括号
5、函数调用的四种方式
1、直调,全局里面直接调用
2、对调,对象里面的函数调用
3、回调,现在不掉回头再掉调(定时器函数,事件函数,ajax)
4、自调,自己调用自己,递归,立即执行函数
6、闭包的概念、作用、缺点
概念:跨作用域访问标识符,这种现象就叫做闭包
作用:减少全局变量的声明,避免了变量名的冲突。一定程度上避免了内存泄漏
缺点:无论如何闭包,都需要一个全局变量来接收或者挂载到window上,如果闭包使用过多,还是有可能会造成内存泄漏
7、面向对象和面向过程的区别和优缺点
面向过程:一步一步完成代码功能,更加注重实现功能的过程步骤。
优点:步骤清晰明了
缺点:不适合大型项目
面向对象:把功能拆分到一个个对象中,更加注重整体和全局的把控
优点:适合做大型项目
缺点:过程步骤不够清晰
8、构造函数的作用和语法
作用:批量创建具有相同属性和方法的对象
语法:1、首字母大写。
2、使用this将属性和方法绑到实例对象上
3、不需要return返回值
4、调用时需要new一下
9、构造函数的4个特点
1、首字母大写。
2、使用this将属性和方法绑到实例对象上
3、不需要return返回值
4、调用时需要new一下
10、new关键字在实例化对象的过程中所起到的作用
1、创建了一个空对象
2、将this指向这个空对象
3、执行构造函数中的代码
4、将创建好的对象以实例对象返回
11、方法过载的含义
方法绑在构造函数上,使用构造函数创建多个实例对象时,每个实例对象都有相同名字的方法,但这些方法又都是独立的,如果创建过多的实例对象就会造成方法过载
12、构造函数原型中原型对象、隐式原型和构造器的概念和作用
1、每一个函数,特指构造函数,都会有一个原型对象
prototype
,挂载到原型对象上的方法可以被共享2、每一个对象都会有1个隐式原型
__proto__
用来指向构造函数的原型对象,只起指向作用,一般不会使用3、每个原型对象中都有一个构造器
constructor
指向构造函数本身
13、原型链的概念并手绘原型链图
概念:1、原型对象也是对象,也有隐式原型
__proto__
,它指向Object的原型对象 2、Object的原型对象的隐式原型指向null
3、Object的原型对象中的constructor指向Object构造函数本身
这些对象和构造函数的原型对象用
__proto__
链接起来,像一条链子一样,最终都会指向null。正是这条链子规定了方法和属性的查找规则,先在自身的构造函数身上查找,找到了就停止查找,找不到就去构造函数的原型对象上查找,还没找到就往Object的原型对象上查找,还没找到就会报错。
原型链图:
14、7种不同执行环境中的this指向
1、全局中,指向window
2、一般函数中:指向window
3、构造函数中:指向实例对象
4、定时器:指向window
5、事件函数:指向事件源
6、对象中:指向调用者
7、箭头函数:指向上一级
15、call、apply和bind方法的区别
call:可以传多个实参
apply:只能传2个实参,第二个实参必须是数组
bind:不会直接调用函数,而是复制了一个改变this指向的新函数
16、基本类型和引用类型在内存中如何分配
基本类型会存入栈中
引用类型的值会存入堆中,地址存入栈中
17、引用类型直接用等号赋值会发生什么情况
所有层级拷贝的都是地址,数据会被共享,改变一个另一个也会跟着改变
18、浅拷贝和深拷贝的含义和语法
浅拷贝:第一层拷贝的是值,其后所有层级拷贝的都是地址
语法:let obj1 = Object.assign({}, obj)
深拷贝:所有的层级拷贝的都是值
语法:let obj1 = JSON.parse(JSON.stringify(obj))
19、检测数据类型的三种方法和区别
typeof:只能检测基本数据类型,语法:typeof 检测数据 | typeof(检测数据)
instanceof:只能检测引用数据类型,语法:检测数据 instanceof 内置构造函数
Object.prototype.toString.call:完美检测所有数据类型,语法:Object.prototype.toString.call(检测数据)
20、es5寄生组合式继承的三个步骤
1、继承属性:在子构造函数中使用call方法调用父构造函数
Father.call(this, 参数1, 参数2…);
2、继承方法:使用Object.create()方法强制让子构造函数的原型对象中的隐式原型指向父构造函数的原型对象
Son.prototype = Object.create(Father.prototype);
3、手动将子构造函数的构造器constructor指回自己
Son.prototype.constructor = Son;
21、手绘终极无敌原型链图
22、手动封装深拷贝
function getType(data) {
return Object.prototype.toString.call(data);
}
// 判断是否是数组
function isArray(arr) {
return getType(arr) === "[object Array]";
}
// 判断是否是对象
function isObj(obj) {
return getType(obj) === "[object Object]"
}
// 判断是否是函数
function isFunc(func) {
return getType(func) === "[object Function]"
}
function cloneDeep(obj) {
let cloneObj = Array.isArray(obj) ? [] : {};
for (let key in obj) {
// 如果是数组或对象就通过递归继续深拷贝
// 递归的使用场景:不知道要使用多少次遍历的时候
if (isArray(obj[key]) || isObj(obj[key])) {
cloneObj[key] = cloneDeep(obj[key]);
} else if (isFunc(obj[key])) {
// 如果是函数就调用bind复制新的
cloneObj[key] = obj[key].bind();
} else {
// 如果是基本数据类型就直接赋值
cloneObj[key] = obj[key];
}
}
return cloneObj;
}