Skip to content

2.手写new和bind #4

@webVueBlog

Description

@webVueBlog

new 运算符:

运算符创建一个用户定义的对象类型的实例 或 具有构造函数的内置对象类型之一。

bind 函数:

方法会创建一个新函数,当这个新函数被调用时,bind()的第一个参数将作为它运行时的this,之后的一系列参数将会传递的实参前传入作为它的参数。

bind函数的特点:

  1. 改变this指向
  2. 第一个参数是this的值,后面的参数是函数接收的参数的值
  3. 返回值不变
// 手写bind
Function.prototype.myBind = function() {
 // 为了获取原函数
 const self = this;

 // 执行slice方法,其中的this指向arguments,得到真正的数组
 const args = Array.prototype.slice.call(arguments);

 // 拿到第一个参数
 const thisValue = args.shift();

 return function() {
  // 返回一个函数,它是一个匿名函数,整个匿名函数,就是保存在变量中的新的函数
  // 返回新函数里执行原函数
  return self.apply(thisValue, args);
 }
}

new原理:

  1. 创建空对象,作为将要返回的对象实例
  2. 指向原型,将这个空对象的原型,指向构造函数的prototype属性
  3. 绑定this,将这个空对象赋值给函数内部的this关键字
  4. 返回新对象,开始执行构造函数内部的代码
// 手写new
function myNew(fn, ...args) {
 // 定义个空对象
 const obj = {};

 // 隐式原型指向构造函数的显式原型
 obj.__proto__ = fn.prototype;

 // 执行构造函数,this指向空对象
 fn.apply(obj, args);

 // 返回对象
 return obj;
}
// new,创建实例,构造函数,剩余参数
const createInstance = (Constructor, ...args) => {
 // 创建对象,并指向构造函数的原型
 const obj = Object.create(Constructor.prototype);

 // 构造函数内部的 this 指向 instance 变量
 let res = Constructor.call(obj, ...args);

// 判断 是否是 函数或对象
const isObj = res !== null && typeof res === 'object';
const isFunc = typeof res === 'function';

 const isFunc || isObj ? res : obj;
}
// objectFactory
function objectFactory() {
 var object = new Object();
 var Constructor = Array.prototype.shift.call(arguments);
 // 原型式继承
 object.__proto__ = Constructor.prototype;
 // arguments第一个已经被shift,此时arguments是我们想要的参数
 var result = Constructor.apply(object, arguments);
 return typeof result === 'object'  ? result : object;
}

new的运行过程:

  1. 新建一个内部对象
  2. 给这个对象指定一个隐式原型链,对象的__proto__指向构造函数的prototype
  3. 指定对象的this
  4. 执行构造函数内部的代码
  5. 如果有返回值,就返回构造函数的返回值,否则就返回新建的对象
function objectFactory() {
 const constructor = [].shift.call(arguments)

 // 新建一个对象,并且将对象的隐式原型指定为构造函数的显示原型
 // 这样就能访问到构造函数的原型中的属性
 let newObject = Object.create(constructor.prototype);
 
 // apply将 this 指定为 newObject
 let res = constructor.apply(newObject, arguments);
 
 // 判断 构造函数 返回的值是否是对象
 return typeof res === 'object' ? res : newObject
}

模拟手写new

// new
function create(Con, ...args) {
 let obj = {}
 // Object.getPrototypeOf(obj) 相当于obj.__proto__
 Object.setProrotypeOf(obj, Con.prototype);
 let result = Con.apply(obj, args)
 return result instanceof Object ? result : obj
}

模拟手写bind

Function.prototype.myBind = function(context) {
 if(typeof this !== 'function') {
  throw new TypeError('Error');
 }
 var _this = this
 var args = [...arguments].slice(1)
 return function F() {
  if (this instanceof F) {
   // instanceof 返回true
  return new _this(...args, ...arguments);
  }
  return _this.apply(content, [...args, ...arguments])
 }
}

new关键字实现了什么

  1. this指向新创建的对象
  2. 对象的[[prototype]]属性指向构造函数的prototype
  3. 执行函数,返回一个新对象
  4. 如果函数没有返回对象类型Object,那么new表达式中的函数调用会自动返回这个新的对象
function objectFactory(func) {
 if(typeof func !== 'functio') {
   throw new Error('请传入一个函数')
 }

 let obj = Object.create(func.prototype);
 let args = [...arguments].slice(1);
 const result = func.apply(obj, args);
 
 let objectResult = typeof result === 'object' && result !== null;
 let funcResult = typeof result === 'function';

 return (objectResult || funcResult) ? result : obj;
}

bind实现了什么

  1. bind返回一个函数
  2. bind可以传入参数
  3. bindFunc返回的函数也可以传入参数
  4. this指向bindObjNew
Function.prototype.myBind = function(context) {
 if(typeof this !== 'function') {
  throw new Error('请输入一个函数');
 }
 let args = [...arguments].slice(1);
 let _this = this;

 let fBound = function() {
  let args2 = [...arguments];
  // return _this.apply(context, [...args, ...args2]);
 return _this.apply(this instanceof fBound ? this : context, [..args, ...args2])
 }
 return fBound; 
}

Bind函数实现:

  1. 返回一个函数
  2. 可以传入参数
Function.prototype.myBind = function(context) {
 if(typeof this != 'function') {
  throw new TypeError('error')
 }
 
 let _this = this
 // 函数剩余的参数
 let args = [...arguments].slice(1)

 return function F() {
  // 因为返回的是一个函数,所以可以 new F(),所以要进行判断
  if(this instanceof F) {
   return new _this(...args, ...arguments);
  }
  return _this.apply(context, args.concat(...arguments));
 }
}

New 实现:

  1. 创建了一个新对象
  2. 链接到原型
  3. 绑定到this
  4. 返回新对象
function objectFactory() {
 // 新创建一个对象
 let obj = {}
 
 // 取出构造函数
 constructor = [].shift.call(arguments);
 // 链接到原型
 obj.__proto__ = constructor.prototype;
 // 绑定到this
 constructor.apply(obj, arguments);
 // 返回新对象
 return obj;
}

new 关键字做了什么?

  1. 一个新的对象
  2. 新对象的__proto__指向构造函数的prototype
  3. 使用apply改变构造函数的this指向,使其指向新对象,这样一来obj就有构造函数里面的属性啦
  4. 判断构造函数是否有返回值,如果构造函数返回对象,则直接返回构造函数中的对象
  5. 返回新对象
function myNew() {
  let obj = new Object();
  let constrctor = Array.prototype.shift.call(arguments);
  obj.__proto__ = constrctor.prototype;
  let res = constrctor.apply(obj, arguments);
  return typeof res === "object" ? res : obj;
}

bind:

Function.prototype.Mybind = function (context) {
  if (typeof this !== "function") return "我们要函数才可以调用哦~";
  let myFn = this;

  // 当前调用传进来的参数
  let arg = Array.prototype.slice.call(arguments, 1);

  let fNOP = function () {};

  let fBound = function () {
    let bindArg = Array.prototype.slice.call(arguments);
    // 当函数作为构造函数时,this会指向他的实例fbound
    // 当函数作为普通函数的时候,this会指向window
    return myFn.apply(
      this instanceof fNOP ? this : context,
      bindArg.concat(arg)
    );
  };
  // 修改函数的原型指向this的原型 实例可以继承绑定函数的原型中的值,因为是引用类型~
  fBound.prototype = this.prototype;
  // 中转一下就不会修改到绑定函数的this啦
  fBound.prototype = new fNOP();
  return fBound;
};

new实践

Object.create = function(o) {
 function f() {}
 f.prototype = o
 return new f;
}
function objectFactory() {
 var obj = new Object(), // 从Object.prototype上克隆一个对象
 Constructor = [].shift.call(arguments); // 取得外部传入的构造器
 
 var F = function() {}
 F.prototype = Constructor.prototype;
 obj = new F(); // 指向正确的原型

 var res = Constructor.apply(obj, arguments); // 借用外部传入的构造器给obj设置属性
 return typeof res === 'object' ? res : obj; // 确保构造器总是返回一个对象
}
Function.prototype.myBind = function(context) {
 if (typeof this !== 'function') {
  throw new Error('Function.prototype.bind - what is trying to be bound is not callable');
 }
 
 var selft = this;
 var args = Array.prototype.slice.call(arguments, 1);
 
 var fNOP = function() {};
 
 var fBound = function() {
  var bindArgs = Array.prototype.slice.call(arguments);
  return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
 }
 fNOP.prototype = this.prototype;
 fBound.prototype = new fNOP();
 return fBound;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions