如何实现 new 、apply 、call 、bind 的底层逻辑

new 原理介绍

new 关键词的主要作用是执行一个构造函数、返回一个实例对象,根据构造函数的怀况中,来确定是否可以接受参数的传递。

function Person() {
  this.name = 'Jack'
}
let p = new Person()
console.log(p.name) // Jack

new 在生成实例的过程中进行的步骤:

  • 创建一个新对象
  • 将构造函数的作用域赋给新对象(this指向新对象)
  • 执行构造函数中的代码(为这个新对象添加属性)
  • 返回新对象

当构造函数中有return一个对象的操作时:

function Person() {
  this.name = 'Jack'
  return{age:18}
}
let 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'
}
let p = new Person()
console.log(p) // {name:'Jack'}
console.log(p.name) // Jack

new 被调用后大致做了哪几件事情

  • 让实例可以访问到私有属性
  • 让实例可以访问构造函数原型(constructor.prototype)所在原型链上的属性
  • 构造函数返回的最后结果是引用数据类型

总结:new关键词执行之后总是会返回一个对象,要么是实例对象,要么是return语句指定的对象

apply & call & bind 原理介绍

call 、apply 和 bind 是挂在 Function 对象上的三个方法,调用这三个方法必须是一个函数

语法:

func.call(thisArg,param1,param2,...)
func.apply(thisArg,param1,param2,...)
func.bind(thisArg,param1,param2,...)

thisArgthis所指向的对象,后面的param1,param2为函数的function的多个参数,如果不需要参数可不写

相同点:都可改变 function 的 this 指向

call & apply 区别:
传参的写法不同

  • apply第二个参数为数组
  • call则是第二个至第N个都是给fcuntion的传参

bind 与 call & apply区别:

  • bind虽然改变了functionthis指向,但不是马上执行
  • call & apply 是在改变functionthis指向后立即执行
借用

A对象有个getName的方法,B 对象也需要临时使用同样的方法,那么这个时候可以借用A 对象的getName方法
例:

let a = {
  name: 'jack',
  getName: function(msg) {
    return msg + this.name
  }
}
let b = {
  name: 'lily'
}
console.log(a.getName('hello~')) // hello~jack
console.log(a.getName.call(b, 'hello~')) // hello~lily
console.log(a.getName.apply(b, ['hello~'])) // hello~lily
let name = a.getName.bind(b, 'hello~')
console.log(name()) // hello~lily

apply & call & bind 的使用场景

1、判断数据类型

Object.prototype.toString几乎可以判断所有类型的数据

function getType(obj) {
  let type = typeof obj
  if (type !== 'object') {
    return type
  }
  return Object.prototype.toString.call(obj).replace(/^$/, '$1')
}

判断数据类型就是借用了Object.prototype.toString方法,最后返回用来判断传入的object字符串来确定最后的数据类型

2、类数组的借用方法

类数组因为不是真正的数组,所以没有数组类型上自带的种种方法,可以利用一些方法云借用数组的方法。

var arrayLike = {
  0: 'java',
  1: 'script',
  length: 2
}
Array.prototype.push.call(arrayLike, 'jack', 'lily')
console.log(typeof arrayLike) // object
console.log(arrayLike) // {0: "java", 1: "script", 2: "jack", 3: "lily", length: 4}
3、获取数组的最大 / 最小值

apply来实现数组中判断最大 / 最小值,apply直接传递数组作为调用方法的参数,也可以减少一步展开数组

let arr = [13, 6, 10, 11, 16]
const max = Math.max.apply(Math, arr)
const min = Math.min.apply(Math, arr)
console.log(max) // 16
console.log(min) // 6
继承
function Parent3() {
  this.name = 'parent3'
  this.play = [1, 2, 3]
}
Parent3.prototype.getName = function() {
  return this.name
}
function Child3() {
  // 第二次调用 Parent3()
  Parent3.call(this)
  this.type = 'child3'
}
Child3.prototype = new Parent3()
// 手动挂上构造器,指向自己的构造函数
Child3.prototype.constructor = Child3
var s3 = new Child3()
s3.play.push(4)
console.log(s3.getName()) //parent3
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
}

这两个方法是直接返回执行结果 ,而 bind 方法是返回一个函数,因此这里直接用 eval 执行得到结果

bind 的实现

bind 的实现思路基本和 apply 一样,但是在最后实现返回结果这里 bind 不需要直接执行,因此不再需要用 eval 而是需要通过返回一个函数的方式将结果返回之后再通过执行这个结果,得到想要的执行效果

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) {
    fbound.prototype = Object.create(this.prototype)
  }
  return fbound
}
总结
总结
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容