call原理
call可以改变一个函数的作用域
let a = {
name: 'foo',
fn: function() {
console.log(this.name);
}
};
let b = {
name: 'bar'
};
a.fn.call(b); // 输出 bar
实现一个自己的call
// 在函数的原型上定义自己的call
Function.prototype.mycall = function(context) {
// context 新作用域 => b
// this 指向要调用的方法 => a.fn
if(typeof this !== 'function'){
throw new TypeError('不是方法!');
}
// 获取参数
let args = [...arguments].slice(1);
// 将要调用的方法添加到新作用域
context.fn = this;
// 在新作用域执行方法
let result = context.fn(...args);
delete context.fn;
// 要调用的方法如果没有返回 result = undefined
return result;
}
从代码中可以看出,call实际就是将要调用的方法添加到新作用域,并执行。
new原理
new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。
new用来创建对象
function Person(name, age) {
this.name = name;
this.age = age;
console.log(this);
}
Person('xm',12); // this指向window
let me = new Person('xg',18); // this指向me
console.log(me.age); // 输出18
- 当使用new关键字时,Person是构造函数
- 不使用new关键字时,Person就是普通函数
实现一个自己的new
function myNew (fun) {
// 假设myNew返回的函数叫 newReturn
return function () {
// 创建一个新对象且将其隐式原型指向构造函数原型
let obj = {
__proto__ : fun.prototype
};
// 执行构造函数
// 根据前面的call原理,构造函数的this指向obj
// 执行完构造函数,obj拥有name,age属性
fun.call(obj, ...arguments);
// 返回该对象
return obj;
}
}
let she = myNew(Person)('xh', 16) // {name: "xh", age: 16}
// myNew(Person) = newReturn
// myNew(Person)('xh', 16) = newReturn('xh', 16)
经典继承
function Rectangle(length,width) {
this.length = length;
this.width = width;
}
Rectangle.prototype.getSize = function() {
return this.length * this.width;
}
function Square(edgeLength) {
// 调用Rectangle构造函数
// Square实例拥有了Rectangle的属性
Rectangle.call(this,edgeLength,edgeLength);
this.getSize = function() {
return this.length * this.width;
}
}
// 创建一个空对象 obj
// 将 obj.__proto__ = Rectangle.prototype
// obj 作为新的上下文执行构造函数
// 返回obj————新的实例
let rect = new Rectangle(3,4);
console.log(rect.getSize());
let square = new Square(5);
console.log(square.getSize());
经典继承也有自身的缺点
- 方法都在构造函数中定义
- 因为方法都在构造函数中定义,所以每次创建实例都会重新赋值一遍方法