1. new
1.1 原理介绍
new 的主要作用就是执行一个构造函数、返回一个实例对象,在 new 的过程中,根据构造函数的情况,来确定是否可以接受参数的传递
function Person(){
this.name = 'Jack';
}
var p = new Person();
console.log(p.name) // Jack
从输出结果可以看出,p 是一个通过 person 这个构造函数生成的一个实例对象
- 创建一个新对象
- 将构造函数的作用域赋给新对象(this 指向新对象)
- 执行构造函数中的代码(为这个新对象添加属性)
- 返回新对象
如果不用 new 这个关键词,结合上面的代码改造一下
function Person() {
this.name = 'Jack';
}
var p = Person();
console.log(p) // undefined
console.log(name) // Jack
console.log(p.name) // 'name' of undefined
没有使用 new 这个关键词,返回的结果就是 undefined。其中由于 JavaScript 代码在默认情况下 this 的指向是 window,那么 name 的输出结果就为 Jack
当构造函数中有 return 一个对象的操作
function Person(){
this.name = 'Jack';
return {age: 18}
}
var p = new Person();
console.log(p) // {age: 18}
console.log(p.name) // undefined
console.log(p.age) // 18
当构造函数最后 return 出来的是一个和 this 无关的对象时,new 命令会直接返回这个新对象,而不是通过 new 执行步骤生成的 this 对象
这里要求构造函数必须是返回一个对象,如果返回的不是对象,那么还是会按照 new 的实现步骤,返回新生成的对象
function Person() {
this.name = 'Jack';
return 'tom';
}
var p = new Person();
console.log(p) // {name: 'Jack'}
console.log(p.name) // Jack
当构造函数中 return 的不是一个对象时,还是会根据 new 关键词的执行逻辑,生成一个新的对象(绑定了最新 this),最后返回出来
总结:new 关键词执行之后总是会返回一个对象,要么是实例对象,要么是 return 语句指定的对象
1.2 实现
new 被调用后大致做了哪几件事情。
- 让实例可以访问到私有属性
- 让实例可以访问构造函数原型(constructor.prototype)所在原型链上的属性
- 构造函数返回的最后结果是引用数据类型
function _new(ctor, ...args) {
if(typeof ctor !== 'function') {
throw 'ctor must be a function';
}
let obj = new Object();
obj.__proto__ = Object.create(ctor.prototype);
let res = ctor.apply(obj, [...args]);
let isObject = typeof res === 'object' && res !== null;
let isFunction = typeof res === 'function';
return isObject || isFunction
? res
: obj;
};
2. apply & call & bind
2.1 原理介绍
call、apply 和 bind 是挂在 Function 对象上的三个方法,调用这三个方法的必须是一个函数
func.call(thisArg, param1, param2, ...)
func.apply(thisArg, [param1,param2,...])
func.bind(thisArg, param1, param2, ...)
其中 func 是要调用的函数,thisArg 一般为 this 所指向的对象,后面的 param1、2 为函数 func 的多个参数,如果 func 不需要参数,则后面的 param1、2 可以不写
call 和 apply 的区别在于,传参的写法不同:apply 的第 2 个参数为数组; call 则是从第 2 个至第 N 个都是给 func 的传参;
而 bind 和(call、apply)又不同,bind 虽然改变了 func 的 this 指向,但不是马上执行,而(call、apply)是在改变了函数的 this 指向之后立马执行
2.2 apply 和 call 的实现
Function.prototype.call = function (context, ...args) {
var context = context || window;
context.fn = this;
var result = eval('context.fn(...args)');
delete context.fn
return result;
}
Function.prototype.apply = function (context, args) {
let context = context || window;
context.fn = this;
let result = eval('context.fn(...args)');
delete context.fn
return result;
}
实现 call 和 apply 的关键就在 eval 这行代码。其中显示了用 context 这个临时变量来指定上下文,然后还是通过执行 eval 来执行 context.fn 这个函数,最后返回 result
这两个方法和 bind 的区别就在于,这两个方法是直接返回执行结果,而 bind 方法是返回一个函数
2.3 bind的实现
Function.prototype.bind = function (context, ...args) {
if (typeof this !== "function") {
throw new Error("this must be a function");
}
var self = this;
var fbound = function () {
self.apply(
this instanceof self
? this
: context,
args.concat(Array.prototype.slice.call(arguments))
);
}
if (this.prototype) { // 在特殊情况下.prototype会缺失
fbound.prototype = Object.create(this.prototype);
}
return fbound;
}
实现 bind 的核心在于返回的时候需要返回一个函数,故这里的 fbound 需要返回,但是在返回的过程中原型链对象上的属性不能丢失。因此这里需要用Object.create 方法,将 this.prototype 上面的属性挂到 fbound 的原型上面,最后再返回 fbound
this instanceof self
( => this instanceof fBound:判断是否使用new操作符来调用的_new):当这个绑定函数被当做普通函数调用的时候,可以直接用context; 而返回的这个之后当做构造函数使用的时候,却是指向这个实例,所以this instanceof self为true时,要用this
网友评论