手动实现new方法,call

Javascript天下第一 !

面试中经常会被问到,new方法实现的原理,你能不能实现一个,在这个框架泛滥的年代,我还是决定沉下心来,自己在把基础知识过一过,自己造一些轮子,沉淀沉淀。

说到new,call,bind的方法,就不得不从原型链开始说起

想起最早接触原型链的时候,看起来特别头疼,特别绕,后来也因为工作,经常写业务代码,渐渐的对这些基础就没怎么用,而且用的也是很普通的给构造函数上添加一个个的方法,更深曾的就没去思考,去实现。

最近学习东西的时候喜欢带着一些问题去学习,去实现自己脑中的想法,然后去带着问题去解决问题,接下来就先介绍下原型链的一些基础知识,帮助大家理解下,可以更好的去实现这些内置方法。介绍方法前,先把一些经常会被问到的问题列出来,去学习的时候想着这些问题,去问问题,然后才能解决问题。

  1. 如何准确判断一个变量是数组
  2. 写一个原型链继承的例子
  3. 描述一下new一个对象的过程,手动实现一个(今天的主题)

问题挺多的,先写这3个问题,接下来简单介绍下原型链

这里我称  __proto__ 为隐式原型,prototype称为显示原型,方便读,方便交流

原型规则

  • 所有的引用类型(数组,对象,函数),都具有对象特性:可自由扩展属性

  • 所有的引用类型(数组,对象,函数),都有一个__ proto__属性,属性值是一个普通对象

  • 所有的函数,都有一个prototype属性,属性值是一个普通的对象

  • 所有的引用类型(数组,对象,函数),__ proto__属性指向它的构造函数的prototype属性值

  • 当试图得到一个引用类型(数组,对象,函数)的某个属性时,如果这个对象本身并没有这个属性,那么会去它的__ proto__(也就是其构造函数的prototype)上去查找

原型链大侄介绍这么多,更多原型链的这里就不赘述了。

new的实现

接下来我们来实现new方法,要实现一个方法,我们先思考这个方法做了什么事情,有输入吗,有输出(返回)吗,需要传参吗,参数类型有要求吗,等...

我们来分析下,new的时候都做了什么事情

  1. 首先,我们会发现new完之后我们会得到一个对象,所以,new的时候会新创建一个空对象,并且最后会把我们创建好的对象给我们的变量,也就变成了一个实例了。
let obj = { };
  1. 然后我们发现,这个new之后生成的对象(实例),它具有构造函数的所有方法以及属性。所以此时new操作做的事情就是把构造函数上的所有属性以及方法都赋值给咱们这个新创建的对象。
obj.__proto__ = fn.prototype    fn指的是构造函数 
  1. 想下前面的操作,我们不是都拿到了构造函数上面所有的方法了吗,但是此时如果直接将这个新对象返回,我们得到的仅仅是一个包含构造函数原型链上方法集合的一个对象。还缺少了当前构造函数内部的属性,所以需要把当前的this值指向新的对象,来拿到构造函数里的所有属性,当然,别忘了传参数。
fn.apply(obj, arguments);
  1. 最后一步,将这个新的对象返回
return obj

总结

所以整合一下上面的思路,简单来写就是4步:

1)创建一个新对象

2)新对象的隐式原型链上挂载构造函数的显示原型链上的方法

3)把this指针指向新的对象

4)把对象返回

完整代码

function _new() {
 // 缓存一个arguments,避免直接修改污染arguments
 let _arguments = arguments;
  // 拿到构造函数,shift处理之后_arguments值已经更改,shift会对原始数据有影响
 let fn = Array.prototype.shift.call(_arguments)            
  // 第一步,创建一个对象
 let target = {};
  // 第二步,链接到原型,给新对象的隐式原型赋予构造函数的显示原型,得到构造函数的所有prototype属性值            
 target.__proto__ = fn.prototype;
  // 第三步,把当前的this指针指向新的对象
 fn.apply(target, _arguments);
  // 第四步,返回当前this   
 return target;
}

call的实现

同样的道理,我们先分析call做了什么事情
首先看下mdn上面对call的使用,它的语法是:

fun.call(thisArg, arg1, arg2, ...)

参数:call方法可以接受很多参数,其中第一个参为在 fun 函数运行时指定的 this 值,后面的是参数列表
作用:call方法提供新的 this 值给当前调用的函数/方法
返回值:使用调用者提供的 this 值和参数调用该函数的返回值,如果该函数没有返回值,则返回undefined,有返回值则返回值为该值
好了,清楚了call的使用方法和作用,咱们来看看它具体是怎么做到的:

  1. 首先,上面提到了this,参数,被调用函数,所以我们应该拿到这些数值
let _this = Array.prototype.shift.call(arguments) 
//这里将拿到传过来的第一个参数,并且改变了当前的参数列表,这里拿到的是this值
//由于[].shift操作会改变原来的数组,所以当前arguments剩下的值就是参数列表,不过是一个类数组

如何拿到被调用的函数呢,这里有两个写法,不过只是用法不同而已

fn._call(this)
//如果是这么调用的话,要获得被调用函数,那么就是当前的this了,因为目前的_call方法是会挂到Function.prototype上
 _call(fn,this)
//如果这么调用的话,就是取参数列表里面的第二个参数值了,看自己怎么传参了~

这里我采用第一种写法,毕竟emmm,比较方便

  1. 现在所需要的东西都有了,this,参数,被调的函数,那么剩下的就很简单了,仔细想下call做了什么,不就是用新的指针去调用被调函数吗,所以,看代码:
_this.fn = this;
//_this是上面从参数列表里面拿到了的新的this指针
//this是当前的方法,也就是被调用的函数
//这段代码的意思是把需要被调用的函数挂载到新的指针下面
  1. 接下来就很简单了,去执行新的方法就好了,不过记得这里有返回值,所以直接返回这个函数执行的结果就好哦~
return _this.fn(...arguments);

完整代码

Function.prototype._call = function () {
  // 不管什么操作,先缓存arguments,避免污染
 let _arguments = arguments;
  // 如果第一个参数是一个null,那么target为全局对象
  // 拿到新的this对象,剩余的都是参数
  let _this = Array.prototype.shift.call(_arguments);
  if (_this == null) target = window;
  // 拿到需要call的方法            
  // 第一步,给当前的target下面添加所需要的方法
  _this.fn = this;
  // 第二步,把参数传给target下面的函数             
  return _this.fn(...arguments);
}
//测试代码
var foo = {
  name: '张三'
}
function info(job, age) {
  console.log('name', this.name)
  console.log(job, age)
}
var name = 'window 张三'
info._call(foo,'web developer',24)        
info._call(null,'web developer',24)

bind的实现

暂且下一期实现

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

推荐阅读更多精彩内容