彷佛生活中经常会遇到这种状况,你去一家公司面试,前面面的都挺好,你以为你对基础算法的了解很好,各类排序,红黑树,二叉树,深度/广度优先算法都答出来了,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); // 能够对比一下,看看二者区别