面试官!你看看我手写的new有没有问题
创建一个空的简单JavaScript对象(即{}); 为步骤1新创建的对象添加属性__proto__,将该属性链接至构造函数的原型对象 ; 将步骤1新创建的对象作为this的上下文 ; 如果该函数没有返回对象,则返回this。???问题 3: 为步骤1新创建的对象添加属性__proto__ 不是很懂,添加__proto__ 的用处在哪里,__proto__这个又是什么个玩意????问题 4: 将该属性链接至构造函数的原型对象 这个操作的意义在哪里????问题 5: 将步骤1新创建的对象作为this的上下文 是不是指的是this指向新创建的对象?我是不是废了?这么多问题不懂?是概念太晦涩,还是我基础太差?上代码 (测试浏览器为谷歌浏览器)看看我们怎么使用new 的
function TestNew(name,age) { this.name = name; this.age = age this.sayHi = function () { alert("hi") }}const testA = new TestNew("月哥",18)console.log(testA.name) // 控制台输出 月哥console.log(testA.name) // 控制台输出 18testA.sayHi() // 控制台输出我创建了一个函数(为啥要大写),new 的话产生了一个什么东西,我把它赋值给了testA,我来看看testA 是个啥,看下图,是一个对象类型,同时它还可以访问到函数TestNew里面的属性和方法image.png
mdn上说这个函数叫做构造函数,啥叫构造函数,这不就是普通的函数吗?这里就不卖官子了,在js中首字母大写的函数,就是构造函数,这是一个约定,其实作用和小写的完全一致,只是做一个区分
我仿佛明白了概念了,尝试解答一下问题 1 new 运算符创建一个用户定义的对象类型的实例 new 创建了一个对象类型的实例,TestNew是我自己定义的,产生的东西可以理解成为我自己定义的对象类型的实例,艹,问题又来了,实例是一个啥子
实例:让我想到了类的实例化,但是那是java这种强类型语言的,js是弱类型语言的,但是我们也常说,实例化构造函数;有点眉目了,实例就是实际的例子,js中就是指实际的对象,实例化的过程就是创建对象的过程,对象类型的实例,反推一下就是,实例就是对象
但是后面这段什么意识:或具有构造函数的内置对象的实例这个还没有搞清楚?
不懂的在构造函数的内置对象是啥?查阅一下文档指的是 Array,Function之类的恍然大悟,原来是这样,new Array(); oh shit!也就是可以这样理解了,自定义的是:new TestNew(),内置的是new Array()之类的,在语法上也解释通了,new constructor[([arguments])] 中的constructor就是一个类(es6)或者函数,可以是内置的,或者自定义的!总结一下:new 创建了一个对象(实例),对象里面有构造函数里面的属性和方法;伪代码
// 自定义的testA = { name: "月哥", age: 18, sayHi: function() { console.log("hi") }}// 构造函数内置的 new Array() 为例子 const testA = new Array()testA = { at: function() {}, concat: function() {}, every: function() {}, ...}问题又来了?构造函数中的this.name 之类的为啥要这样写,按道理来说,这个不是挂在全局作用域里面了吗? 难受啊!怎么这么多问题!这个是???问题 6了继续解决问题刚刚我们解答了问题1,问题2,现在来解答问题3-5,这些问题都是new做了哪些事产生的,问题 3 __proto__这个是什么玩意,给对象添加这个属性有什么作用,我们带着问题去寻找答案!proto 我查了一些书籍。类似红宝书,当调用构造函数创建一个新的实例后,该实例的内部将会包含一个指针(内部属性),指向构造函数的原型对象,ECMA-262第五版中管这个指针叫[[prototype]],虽然在脚本中没有明确的方式访问[[prototype]],但是在fx,sf,chrome在每个对象上都支持一个属性__proto__;抄书真累,红宝书对上面__proto__的解析很明确,感觉很高端的样子,总结一下:也就是每个对象实例创建的时候都会有一个内部属性__proto__,指向这个函数的原型对象,__proto__会有浏览器兼容性问题,同时在mdn上被标记已经废弃,大家不要在生产环境上使用! 看下面的图片,每个对象上面都有__proto__属性问题 4: 将该属性链接至构造函数的原型对象 这个操作的意义在哪里?
// 伪代码// 创建一个新的对象const newObj = new Object()// 为步骤1新创建的对象添加属性__proto__,将该属性链接至构造函数的原型对象 ;newObj.__proto__ = TestNew.prototype查阅文档得知:这段代码的意识是,构造函数内部的 this 被赋值为这个新对象(即 this 指向新对象)。但是这样做this指向新的对象,指向它干嘛?指向它就是把这个构造函数的作用域赋给新对象,此时这个对象还没有构造函数里面的属性和方法,这一步的操作就是将this指向这个新的对象,为接下来的操作铺路,单独看这段代码确实让很多同学有点闷逼!我们把代码拉出来就是:好我们接着看,算是勉强解释了问题4,同时把问题 5,也解释了,脱离上下文解析感觉就有点强行解释一波了,还是不够充分,最后一步就是返回这个新的对象,ok ,我们来观摩网上各位大佬实现new的代码,
function _new(ctor, ...args) { if(typeof ctor !== "function") { throw "ctor must be a function"; } let obj = new Object(); obj.__proto__ = Object.create(TestNew.prototype); let res = ctor.apply(obj, [...args]); let isObject = typeof res === "object" && res !== nu ll; let isFunction = typeof res === "function"; return isObject || isFunction ? res : obj;};这里还有隐藏的操作,如果函数直接return出来了值,结果又不一样了,先记下来,等我们测试代码的时候讲!问题 c:的解答同时也解答了问题 6其他实现中有同学用Object.setPrototypeOf;MDN上不建议用,因为性能比较差。推荐用Object.create()创建对象但是到这里可以解释一下问题 6了, 通过apply这个操作,强行把函数内部的this指向创建的新的对象,从而让我们思考,为什么在一般的函数里面很少能够看到里面写this.name = name之类的东西,因为这种操作是为了配合new 做使用;如果单独写,单独调用的话,这个人绝对要脑子不太好使(手动滑稽一波,哈哈)
代码写的不错,我测试了下,和new的功能基本一致但是我有来找茬了,我有几个问题:
问题 a: 你是实现new这个操作,为何里面还写new Object(),这波操作有点骚啊!
问题 b: obj.proto = Object.create(TestNew.prototype);在这个代码中,为什么要用object.create(),直接写TestNew.prototype不行吗?
问题 c: let res = ctor.apply(obj, [...args]);这个操作是为了执行构造函数的代码,为这个新的对象添加属性和方法,这样写,我觉得很多同学有点看不懂了,为什么这样写就能够给新的对象添加属性和方法呢?
最后一个判断OK,我能看懂;总算是找回了一点信心了!接着往下看
问题 a :这一波操作我觉得挺有争议的,我觉得你不应该在实现new 里面使用new,那你还实现个锤锤!那我们直接换成 let obj = {},这样是不是更加的合适些,先不要管有没有问题,强行先把第一个问题解决了!
问题 b : Object.create()是做啥的,方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__;返回值:一个新对象,带着指定的原型对象和属性。详情请看mdn object.create()链接or (红宝书第170页)但是看了一圈,用这个操作具体的好处是啥(在当前代码中),难道就是使用现有的对象来提供新创建的对象的__proto__ 这个吗?难道直接用TestNew.prototype不行吗?问题还在继续,没有做有说服力的解答
问题 c:let res = ctor.apply(obj, [...args]),使用apply的作用就是为了了让构造函数内部的this指向obj,当指向obj的时候,this.name 等同与 obj.name,这样就非常清晰了,后面的参数传到函数中,正常执行就好,比方ctor === TextNew ,TextNew("月哥",18) 就等同于 obj.name = "月哥",obj.age = 18;
第一版深入聊一下问题 b基于上面的解析我们来实现第二版第二版function _new1(ctor, ...args) { if(typeof ctor !== "function") { throw "ctor must be a function"; } // let obj = new Object(); let obj = {}; // obj.__proto__ = Object.create(ctor.prototype); obj.__proto__ = ctor.prototype let res = ctor.apply(obj, [...args]); let isObject = typeof res === "object" && res !== nu ll; let isFunction = typeof res === "function"; return isObject || isFunction ? res : obj;};开始测试
测试环境:google浏览器:版本 100.0.4896.88(正式版本)
测试数据:name: "月哥",age: 18 ,sayHi(),原型上的sayGood()
测试范围:new, _new(),_new1()
测试用例:输出 实例对象,输出 name ,输出age,调用sayHi方法
预期结果:_new() & _new1(),表现形式与new 保持一致
测试代码:
function TestNew(name,age) { this.name = name; this.age = age this.sayHi = function () { console.log("hi") }}TestNew.prototype.sayGood = function () { console.log("你好!点个赞再走!")}function _new(ctor, ...args) { if(typeof ctor !== "function") { throw "ctor must be a function"; } let obj = new Object(); obj.__proto__ = Object.create(TestNew.prototype); let res = ctor.apply(obj, [...args]); let isObject = typeof res === "object" && res !== null; let isFunction = typeof res === "function"; return isObject || isFunction ? res : obj;};function _new1(ctor, ...args) { if(typeof ctor !== "function") { throw "ctor must be a function"; } let obj = {}; obj.__proto__ = ctor.prototype let res = ctor.apply(obj, [...args]); let isObject = typeof res === "object" && res !== null; let isFunction = typeof res === "function"; return isObject || isFunction ? res : obj;};const testNew = new TestNew("月哥",18)const test_New = _new(TestNew,"月哥",18)const test_New1 = _new1(TestNew,"月哥",18)console.log("---使用new测试结果----")console.log(testNew)console.log(testNew.age)console.log(testNew.name)testNew.sayHi();testNew.sayGood()console.log("---使用_new测试结果----")console.log(test_New)console.log(test_New.age)console.log(test_New.name)testNew.sayHi();testNew.sayGood()console.log("---使用_new1测试结果----")console.log(test_New1)console.log(test_New1.age)console.log(test_New1.name)test_New1.sayHi();test_New1.sayGood()测试结果:image.pngimage.pngimage.png细心的同学发了问题了吗???不测不知道 ,使用:obj.proto = Object.create(TestNew.prototype)的_new 实例打印出来是两层的[[Prototype]];new,和_new1表现形式一致,也就意味着在表现形式上,使用object.create()看起来是有那么问题的;so这样 obj = Object.create(TestNew.prototype)问题的原因是多了一层的原因是因为 Object.create(TestNew.prototype) 是基于目标对象TestNew.prototype创建出一个实例,概念上使用现有的对象来提供新创建的对象的__proto__([[Prototype]] 等价__proto__,有的浏览器会是两层__proto__)结论来了,可以不用Object.create()来指定原型,这样的表现形式会多一层__proto__好处没发现,坏处发现了,我们一般常用,Object.create(null)用来创建一个纯净的对象,直接上代码看一下,很清晰!image.png边界测试修改TestNew()
function TestNew(name,age) { this.name = name; this.age = age this.sayHi = function () { alert("hi") } return null // return 1 // return "1" // return true // return {} // return function(){} // return /a/}结果基础数据类型直接正常返回值,引用数据类型,直接返回函数内返回的值,同时,函数上的方法报错,其他截图就不贴了,大家可以自己看下image.png总结上面说了这么多其实就一句话:new的核心作用:创建产生一个新的实例对象,这个实例对象拥有函数的属性和方法面试中你直接可以这样说了,你们可能问为啥这么短,new的细节这么多,而且为啥不说mdn上对new的概念,但是你要记住,你要形成你自己的思考,你自己的沉淀(哈哈,满满的阿里味,细细想来,好像说的也挺对!)很多时候你说的多不一定说的对,一定要抓住重点,而且中华文化博大精深,我说了核心作用,话是没有说死了,如果面试官继续追问,直接就可以顺着话往下面说,什么原型啥的,我都没有点,但是你问我可以讲的很明白,要是不熟,不要给你自己挖坑!如果文章看不懂视频也配套出来了在B站,月哥一步一步带你手写new;地址点击原文链接或B站搜索:前端要努力,感谢关注!
相关阅读
-
世界热推荐:今晚7:00直播丨下一个突破...
今晚19:00,Cocos视频号直播马上点击【预约】啦↓↓↓在运营了三年... -
NFT周刊|Magic Eden宣布支持Polygon网...
Block-986在NFT这样的市场,每周都会有相当多项目起起伏伏。在过去... -
环球今亮点!头条观察 | DeFi的兴衰与...
在比特币得到机构关注之后,许多财务专家预测世界将因为加密货币的... -
重新审视合作,体育Crypto的可靠关系才能双赢
Block-987即使在体育Crypto领域,人们的目光仍然集中在FTX上。随着... -
简讯:前端单元测试,更进一步
前端测试@2022如果从2014年Jest的第一个版本发布开始计算,前端开发... -
焦点热讯:刘强东这波操作秀
近日,刘强东发布京东全员信,信中提到:自2023年1月1日起,逐步为...