摘要:接下來繼續看升級版例子例子軒轅軒轅軒轅是瀏覽器實現的查看原型方案。模擬實現知道了這些現象,我們就可以模擬實現操作符。
前言
用過Vuejs的同學都知道,需要用new操作符來實例化。
new Vue({ el: "#app", mounted(){}, });
那么面試官可能會問是否想過new到底做了什么,怎么模擬實現呢。
附上之前寫文章寫過的一段話:已經有很多模擬實現new操作符的文章,為什么自己還要寫一遍呢。學習就好比是座大山,人們沿著不同的路登山,分享著自己看到的風景。你不一定能看到別人看到的風景,體會到別人的心情。只有自己去登山,才能看到不一樣的風景,體會才更加深刻。new 做了什么
先看簡單例子1:
// 例子1 function Student(){ } var student = new Student(); console.log(student); // {} // student 是一個對象。 console.log(Object.prototype.toString.call(student)); // [object Object] // 我們知道平時聲明對象也可以用new Object(); 只是看起來更復雜 // 順便提一下 `new Object`(不推薦)和Object()也是一樣的效果 // 可以猜測內部做了一次判斷,用new調用 /** if (!(this instanceof Object)) { * return new Object(); * } */ var obj = new Object(); console.log(obj) // {} console.log(Object.prototype.toString.call(student)); // [object Object] typeof Student === "function" // true typeof Object === "function" // true
從這里例子中,我們可以看出:一個函數用new操作符來調用后,生成了一個全新的對象。而且Student和Object都是函數,只不過Student是我們自定義的,Object是JS本身就內置的。
再來看下控制臺輸出圖,感興趣的讀者可以在控制臺試試。
與new Object() 生成的對象不同的是new Student()生成的對象中間還嵌套了一層__proto__,它的constructor是Student這個函數。
// 也就是說: student.constructor === Student; Student.prototype.constructor === Student;小結1:從這個簡單例子來看,new操作符做了兩件事:
創建了一個全新的對象。
這個對象會被執行[[Prototype]](也就是__proto__)鏈接。
接下來我們再來看升級版的例子2:
// 例子2 function Student(name){ console.log("賦值前-this", this); // {} this.name = name; console.log("賦值后-this", this); // {name: "軒轅Rowboat"} } var student = new Student("軒轅Rowboat"); console.log(student); // {name: "軒轅Rowboat"}
由此可以看出:這里Student函數中的this指向new Student()生成的對象student。
小結2:從這個例子來看,new操作符又做了一件事:生成的新對象會綁定到函數調用的this。
接下來繼續看升級版例子3:
// 例子3 function Student(name){ this.name = name; // this.doSth(); } Student.prototype.doSth = function() { console.log(this.name); }; var student1 = new Student("軒轅"); var student2 = new Student("Rowboat"); console.log(student1, student1.doSth()); // {name: "軒轅"} "軒轅" console.log(student2, student2.doSth()); // {name: "Rowboat"} "Rowboat" student1.__proto__ === Student.prototype; // true student2.__proto__ === Student.prototype; // true // __proto__ 是瀏覽器實現的查看原型方案。 // 用ES5 則是: Object.getPrototypeOf(student1) === Student.prototype; // true Object.getPrototypeOf(student2) === Student.prototype; // true
關于JS的原型關系筆者之前看到這張圖,覺得很不錯,分享給大家。
也就是這個對象會被執行[[Prototype]](也就是__proto__)鏈接。并且通過new Student()創建的每個對象將最終被[[Prototype]]鏈接到這個Student.protytype對象上。
細心的同學可能會發現這三個例子中的函數都沒有返回值。那么有返回值會是怎樣的情形呢。
那么接下來請看例子4
// 例子4 function Student(name){ this.name = name; // Null(空) null // Undefined(未定義) undefined // Number(數字) 1 // String(字符串)"1" // Boolean(布爾) true // Symbol(符號)(第六版新增) symbol // Object(對象) {} // Function(函數) function(){} // Array(數組) [] // Date(日期) new Date() // RegExp(正則表達式)/a/ // Error (錯誤) new Error() // return /a/; } var student = new Student("軒轅Rowboat"); console.log(student); {name: "軒轅Rowboat"}
筆者測試這七種類型后MDN JavaScript類型,得出的結果是:前面六種基本類型都會正常返回{name: "軒轅Rowboat"},后面的Object(包含Functoin, Array, Date, RegExg, Error)都會直接返回這些值。
由此得出 小結4:如果函數沒有返回對象類型Object(包含Functoin, Array, Date, RegExg, Error),那么new表達式中的函數調用會自動返回這個新的對象。
結合這些小結,整理在一起就是:
創建了一個全新的對象。
這個對象會被執行[[Prototype]](也就是__proto__)鏈接。
生成的新對象會綁定到函數調用的this。
通過new創建的每個對象將最終被[[Prototype]]鏈接到這個函數的prototype對象上。
如果函數沒有返回對象類型Object(包含Functoin, Array, Date, RegExg, Error),那么new表達式中的函數調用會自動返回這個新的對象。
new 模擬實現知道了這些現象,我們就可以模擬實現new操作符。直接貼出代碼和注釋
/** * 模擬實現 new 操作符 * @param {Function} ctor [構造函數] * @return {Object|Function|Regex|Date|Error} [返回結果] */ function newOperator(ctor){ if(typeof ctor !== "function"){ throw "newOperator function the first param must be a function"; } // ES6 new.target 是指向構造函數 newOperator.target = ctor; // 1.創建一個全新的對象, // 2.并且執行[[Prototype]]鏈接 // 4.通過`new`創建的每個對象將最終被`[[Prototype]]`鏈接到這個函數的`prototype`對象上。 var newObj = Object.create(ctor.prototype); // ES5 arguments轉成數組 當然也可以用ES6 [...arguments], Aarry.from(arguments); // 除去ctor構造函數的其余參數 var argsArr = [].slice.call(arguments, 1); // 3.生成的新對象會綁定到函數調用的`this`。 // 獲取到ctor函數返回結果 var ctorReturnResult = ctor.apply(newObj, argsArr); // 小結4 中這些類型中合并起來只有Object和Function兩種類型 typeof null 也是"object"所以要不等于null,排除null var isObject = typeof ctorReturnResult === "object" && ctorReturnResult !== null; var isFunction = typeof ctorReturnResult === "function"; if(isObject || isFunction){ return ctorReturnResult; } // 5.如果函數沒有返回對象類型`Object`(包含`Functoin`, `Array`, `Date`, `RegExg`, `Error`),那么`new`表達式中的函數調用會自動返回這個新的對象。 return newObj; }
最后用模擬實現的newOperator函數驗證下之前的例子3:
// 例子3 多加一個參數 function Student(name, age){ this.name = name; this.age = age; // this.doSth(); // return Error(); } Student.prototype.doSth = function() { console.log(this.name); }; var student1 = newOperator(Student, "軒轅", 18); var student2 = newOperator(Student, "Rowboat", 18); // var student1 = new Student("軒轅"); // var student2 = new Student("Rowboat"); console.log(student1, student1.doSth()); // {name: "軒轅"} "軒轅" console.log(student2, student2.doSth()); // {name: "Rowboat"} "Rowboat" student1.__proto__ === Student.prototype; // true student2.__proto__ === Student.prototype; // true // __proto__ 是瀏覽器實現的查看原型方案。 // 用ES5 則是: Object.getPrototypeOf(student1) === Student.prototype; // true Object.getPrototypeOf(student2) === Student.prototype; // true
可以看出,很符合new操作符。讀者發現有不妥或可改善之處,歡迎指出。
回顧這個模擬new函數newOperator實現,最大的功臣當屬于Object.create()這個ES5提供的API。
筆者之前整理的一篇文章中也有講過,可以翻看JavaScript 對象所有API解析
MDN Object.create()
Object.create(proto, [propertiesObject])
方法創建一個新對象,使用現有的對象來提供新創建的對象的__proto__。
它接收兩個參數,不過第二個可選參數是屬性描述符(不常用,默認是undefined)。
var anotherObject = { name: "軒轅Rowboat" }; var myObject = Object.create(anotherObject, { age: { value:18, }, }); // 獲得它的原型 Object.getPrototypeOf(anotherObject) === Object.prototype; // true 說明anotherObject的原型是Object.prototype Object.getPrototypeOf(myObject); // {name: "軒轅Rowboat"} // 說明myObject的原型是{name: "軒轅Rowboat"} myObject.hasOwnProperty("name"); // false; 說明name是原型上的。 myObject.hasOwnProperty("age"); // true 說明age是自身的 myObject.name; // "軒轅Rowboat" myObject.age; // 18;
對于不支持ES5的瀏覽器,MDN上提供了ployfill方案。
if (typeof Object.create !== "function") { Object.create = function (proto, propertiesObject) { if (typeof proto !== "object" && typeof proto !== "function") { throw new TypeError("Object prototype may only be an Object: " + proto); } else if (proto === null) { throw new Error("This browser"s implementation of Object.create is a shim and doesn"t support "null" as the first argument."); } if (typeof propertiesObject != "undefined") throw new Error("This browser"s implementation of Object.create is a shim and doesn"t support a second argument."); function F() {} F.prototype = proto; return new F(); }; }
到此,文章就基本寫完了。感謝讀者看到這里。
最后總結一下:1.new做了什么:
創建了一個全新的對象。
這個對象會被執行[[Prototype]](也就是__proto__)鏈接。
生成的新對象會綁定到函數調用的this。
通過new創建的每個對象將最終被[[Prototype]]鏈接到這個函數的prototype對象上。
如果函數沒有返回對象類型Object(包含Functoin, Array, Date, RegExg, Error),那么new表達式中的函數調用會自動返回這個新的對象。
2.怎么模擬實現:
// 去除了注釋 function newOperator(ctor){ if(typeof ctor !== "function"){ throw "newOperator function the first param must be a function"; } newOperator.target = ctor; var newObj = Object.create(ctor.prototype); var argsArr = [].slice.call(arguments, 1); var ctorReturnResult = ctor.apply(newObj, argsArr); var isObject = typeof ctorReturnResult === "object" && ctorReturnResult !== null; var isFunction = typeof ctorReturnResult === "function"; if(isObject || isFunction){ return ctorReturnResult; } return newObj; }
讀者發現有不妥或可改善之處,歡迎指出。另外覺得寫得不錯,可以點個贊,也是對筆者的一種支持。
關于作者:常以軒轅Rowboat若川為名混跡于江湖。前端路上 | PPT愛好者 | 所知甚少,唯善學。
個人博客
segmentfault前端視野專欄,開通了前端視野專欄,歡迎關注
掘金專欄,歡迎關注
知乎前端視野專欄,開通了前端視野專欄,歡迎關注
github,歡迎follow~
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/99018.html
摘要:點擊那么面試官可能會問是否想過到底做了什么,怎么模擬實現呢。另外前不久寫過一篇文章面試官問能否模擬實現的操作符。所以相當于調用時,的返回值函數內部要模擬實現實現的操作。文章中的例子和測試代碼放在中模擬實現。 前言 用過React的同學都知道,經常會使用bind來綁定this。 import React, { Component } from react; class TodoItem ...
摘要:之前寫過兩篇面試官問能否模擬實現的操作符和面試官問能否模擬實現的方法其中模擬方法時是使用的和修改指向。但面試官可能問能否不用和來實現呢。使用模擬實現的瀏覽器環境非嚴格模式方法的屬性是。 之前寫過兩篇《面試官問:能否模擬實現JS的new操作符》和《面試官問:能否模擬實現JS的bind方法》 其中模擬bind方法時是使用的call和apply修改this指向。但面試官可能問:能否不用cal...
摘要:之前寫過一篇文章面試官問能否模擬實現的和方法就是利用對象上的函數指向這個對象,來模擬實現和的。雖然實際使用時不會顯示返回,但面試官會問到。非嚴格模式下,和,指向全局對象 前言 面試官出很多考題,基本都會變著方式來考察this指向,看候選人對JS基礎知識是否扎實。讀者可以先拉到底部看總結,再谷歌(或各技術平臺)搜索幾篇類似文章,看筆者寫的文章和別人有什么不同(歡迎在評論區評論不同之處),...
摘要:譯立即執行函數表達式處理支持瀏覽器環境微信小程序。學習整體架構,利于打造屬于自己的函數式編程類庫。下一篇文章可能是學習的源碼整體架構。也可以加微信,注明來源,拉您進前端視野交流群。 前言 上一篇文章寫了jQuery整體架構,學習 jQuery 源碼整體架構,打造屬于自己的 js 類庫 雖然看過挺多underscore.js分析類的文章,但總感覺少點什么。這也許就是紙上得來終覺淺,絕知此...
摘要:用過的讀者知道,經常用繼承。部分源碼使用點擊這里查看源碼面試官可以順著這個問繼承的相關問題,比如的繼承用如何實現。主要就是三點子類構造函數的指向父類構造器,繼承父類的靜態方法子類構造函數的的指向父類構造器的,繼承父類的方法。 用過React的讀者知道,經常用extends繼承React.Component。 // 部分源碼 function Component(props, conte...
閱讀 1369·2021-10-19 11:42
閱讀 717·2021-09-22 16:04
閱讀 1867·2021-09-10 11:23
閱讀 1838·2021-07-29 14:48
閱讀 1247·2021-07-26 23:38
閱讀 2812·2019-08-30 15:54
閱讀 1024·2019-08-30 11:25
閱讀 1694·2019-08-29 17:23