彷佛生活中經常會遇到這種狀況,你去一家公司面試,前面面的都挺好,你以爲你對基礎算法的瞭解很好,各類排序,紅黑樹,二叉樹,深度/廣度優先算法都答出來了,leetcode上的若干困難題目也都答上來了,而後面試官說,"那麼好吧,介紹一下你對原型的見解吧。"
???我頭髮。我leetcode上刷了100天,我費勁心思研究了各類算法和數據結構,你叫我講講原型?面試
爲了應對這種狀況,本文經過以下代碼展現下js的原型僅供參考。算法
const c = (...v) => console.log(...v); function People(name, age) { this.name = name; this.age = age; } /*你想這麼寫也不要緊 const People = function(name, age) { this.name = name; this.age = age; } */ People.prototype.info = function() { c(`My name is ${this.name}, my age is ${this.age}.`); }; // 在原型上定義方法 function Student(name, age, school) { People.call(this, ...arguments); this.school = school; } Student.prototype = People.prototype; // 這裏推薦用new People(),直接指定People.prototype會污染People原型,以下結果 Student.prototype.constructor = Student; //修正constructor指向 Student.prototype.talk = function() { c(`My school is ${this.school}`); }; // 須要在改變了Student的原型後定義,不然沒法獲取到該方法 const xiaoD = new Student("xiaoD", 4, "小星星幼兒園"); xiaoD.info(); xiaoD.talk(); const somebody = new People("somebody", 22); somebody.talk(); c(xiaoD.__proto__ === Student.prototype); c(Student.__proto__ === Function.prototype); c(Function.prototype === Function.__proto__); c(Function.__proto__ === Object.__proto__); c(Object.__proto__ === Function.prototype); c(Object.prototype === Function.prototype.__proto__); c(Object.prototype.__proto__ === null);
能夠先猜一下是什麼結果。。數據結構
好吧,不用猜了。結果以下app
My name is xiaoD, my age is 4. My school is 小星星幼兒園 My school is undefined true true true true true true true
每一個實例對象( object )都有一個私有屬性(稱之爲 proto )指向它的原型對象( prototype )。該原型對象也有一個本身的原型對象( proto ) ,層層向上直到一個對象的原型對象爲 null
。根據定義,null
沒有原型,並做爲這個原型鏈中的最後一個環節。至於誰指誰,應該從上面的代碼中就能夠清晰的看出來了。這裏注意的是隻有函數中才有 prototype 屬性。函數
class People { constructor(name, age) { this.name = name; this.age = age; } info() { c(`My name is ${this.name}, my age is ${this.age}.`); } } class Student extends People { constructor(name, age, school) { super(...arguments); // 繼承屬性 this.school = school; this.talk = this.talk.bind(this); // 綁定this /* 或者這樣綁定 this.talk = () => { this.info(); // 箭頭函數中的this在定義時綁定 c(`My school is ${this.school}`); }; */ } talk() { this.info(); c(`My school is ${this.school}`); } } const xiaoD = new Student("xiaoD", 4, "小星星幼兒園"); xiaoD.talk(); const { talk } = xiaoD; talk(); // 不綁定this這裏會報錯 const somebody = new People("somebody", 22); somebody.talk(); // 報錯,父類中沒有該方法
這裏有三個注意點:this
talk
方法中的this
,默認指向Student
類的實例。可是,若是將這個方法提取出來單獨使用,this
會指向該方法運行時所在的環境(因爲 class 內部是嚴格模式,因此 this 實際指向的是undefined
),從而致使找不到info
方法而報錯。解決辦法如代碼中所示。(那麼問題又來了,手寫個apply,call,bind的polyfill吧)prototype
const obj = { name: "xiaoD" }; const fn = function(...args) { c(this.name, ...args); }; Function.prototype.myApply = function(obj, [...args]) { return this.call(obj, ...args); }; fn.myApply(obj, ["真", "棒"]); // xiaoD 真 棒 fn.apply(obj, ["真", "棒"]); // xiaoD 真 棒
拿走,不謝。code
Function.prototype.myApply = function(obj, [...args]) { obj.fn = this; let ret = obj.fn(...args); delete obj.fn; return ret; }; Function.prototype.myBind = function(obj) { obj.fn = this; return function(...args) { return obj.fn(...args); }; }; // 偷偷貼在這裏
知道了原型、原型鏈,那new一個對象的過程知道嗎,能手寫一個嗎。對象
new一個對象的過程大概分紅三步:排序
const myNew = function(fn, ...args) { let obj = {}; obj.__proto__ = fn.prototype; let ret = fn.call(obj, ...args); return ret ? ret : obj; }; function People(name, age) { this.name = name; this.age = age; } let xiaoD = myNew(People, "xiaoD", 4); let xiaoY = new People("xiaoY", 4); // 能夠對比一下,看看二者區別