手写一个 new
new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。new 关键字会进行如下的操作:
- 1.创建一个空的简单JavaScript对象(即{ } );
- 2.链接该对象(即设置该对象的构造函数)到另一个对象 ;
- 3.将步骤1新创建的对象作为this的上下文 ;
- 4.如果该函数没有返回对象,则返回this。
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
const car1 = new Car('Eagle', 'Talon TSi', 1993);
console.log(car1.make);
手写new
的实现
function myNew() {
// 1、获得构造函数,
var context = arguments[0]
// 2、创建一个空的对象并链接到原型,obj 可以访问构造函数原型中的属性
let obj = Object.create(context.prototype);
// 3、绑定 this 实现继承,obj 可以访问到构造函数中的属性
let args = [...arguments].slice(1)
let result = context.apply(obj, args);
// 4、优先返回构造函数返回的对象
return result instanceof Object ? result : obj;
}
function Per(make, name, age) {
this.make = make;
this.name = name;
this.age = age;
}
const person = myNew(Per, '男', '小菜', 18);
// const person = new Per('男', '小菜', 18);
console.log(person, 'result');
警告: 通过现代浏览器的操作属性的便利性,可以改变一个对象的 [[Prototype]] 属性, 这种行为在每一个JavaScript引擎和浏览器中都是一个非常慢且影响性能的操作,使用这种方式来改变和继承属性是对性能影响非常严重的,并且性能消耗的时间也不是简单的花费在
obj.__proto__
= … 语句上, 它还会影响到所有继承来自该 [[Prototype]] 的对象,如果你关心性能,你就不应该在一个对象中修改它的 [[Prototype]]。相反, 创建一个新的且可以继承 [[Prototype]] 的对象,推荐使用Object.create()
MDN:new运算符
手写一个call
- 1.context 存在就使用 context,否则是 window
- 2.使用 Object(context) 将 context 转换成对象,并通过 context.fn 将 this 指向 context
- 3.将函数设置为对象的属性
- 4.执行该函数
- 5.拿到结果返回前,删除掉 fn
Function.prototype.call1 = function (context) {
// 将方法挂载到对象上
var context = context || window;
context.fn = this
// 执行对象的这个方法
let args = [...arguments].slice(1)
let result = context.fn(...args)
// 将这个方法从对象上删除
delete context.fn
return result
}
手写一个apply
- 将方法挂载到对象上
- 执行对象的这个方法
- 将这个方法从对象上删除
Function.prototype.apply1 = function (context) {
// 将方法挂载到对象上
var context = context || window;
context.fn = this;
// 执行对象的这个方法
let args = arguments[1]
let result;
if (args) {
result = context.fn(...args)
} else {
result = context.fn()
}
//将这个方法从对象上删除
delete context.fn
return result
}
手写一个bind
- 1.bind 的参数可以在绑定和调用的时候分两次传入
- 2.bindArgs 是绑定时除了第一个参数以外传入的参数,args 是调用时候传入的参数,将二者拼接后一起传入
- 3.如果使用 new 运算符构造绑定函数,则会改变 this 指向,this 指向当前的实例
- 4.通过 Fn 链接原型,这样 myBind 就可以通过原型链访问父类 Fn 的属性
Function.prototype.myBind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('this is not function')
}
let that = this
let args = [...arguments].slice(1)
return function Fn() {
// 判断是否被当做构造函数使用
if (this instanceof Fn) {
return that.apply(this, args.concat([...arguments]))
} else {
return that.apply(context, args.concat([...arguments]))
}
}
}
let obj = {
name: '小菜'
}
function foo(a, b, c) {
return {
name: this.name,
a,
b,
c
}
}
foo.myBind(obj, 1, 2, 3)();
console.log(foo.myBind(obj, 1, 2, 3)())
// console.log(foo.bind(obj, 1, 2, 3)())
手写 instanceOf
- 1.在
left
的原型链中层层查找,是否有原型等于prototype
- 2.确定边界条件,如果
left === null
,即找到头没找到返回false
,right === left
,即找到返回true
- 3.
left = left.__proto__
,不停的向上查找
const myInstanceof = function(left, right) {
right = right.prototype;
left = left.__proto__;
while (true) {
if (left === null) {
return false;
}
if (right === left) {
return true;
}
left = left.__proto__;
}
}
手写 Object.create
- 新建一个空的构造函数 F ,然后让 F.prototype 指向 obj,最后返回 F 的实例
const myCreate = function (obj) {
function F() {};
F.prototype = obj;
return new F();
}