关于原型、原型链、和继续

好像生涯中常常会碰到这类状况,你去一家公司口试,前面面的都挺好,你以为你对基本算法的相识很好,种种排序,红黑树,二叉树,深度/广度优先算法都答出来了,leetcode上的多少难题题目也都答上来了,然后口试官说,”那末好吧,引见一下你对原型的意见吧。”

???我头发。我leetcode上刷了100天,我费力心机研讨了种种算法和数据结构,你叫我讲讲原型?

为了应对这类状况,本文经由历程以下代码展现下js的原型仅供参考。

上CODE

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);

能够先猜一下是什么效果。。

好吧,不必猜了。效果以下

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();  // 报错,父类中没有该要领

这里有三个注重点:

  • 父类中不会存在子类的要领(面向对象六大准绳还记得吗,开闭,单一职责,依靠倒转,里氏置换,晓得起码,接口断绝,合成聚合复用)
  • 上面代码中,talk要领中的this,默许指向Student类的实例。然则,假如将这个要领提取出来零丁运用,this会指向该要领运行时地点的环境(因为 class 内部是严厉形式,所以 this 现实指向的是undefined),从而致使找不到info要领而报错。解决办法如代码中所示。
  • 关于箭头函数的this指向题目,尽人皆知,一般函数的this是指向挪用它的谁人对象,所以一般函数能够经由历程apply,call,bind来转变this的指向。而箭头函数中的this是在定义函数的时刻就已肯定了,指向外层作用域链中的一般函数,若没有,this则指向undefined。

PS

(那末题目又来了,手写个apply,call,bind的polyfill吧)

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 真 棒

拿走,不谢。

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);
  };
};
// 偷偷贴在这里

再PS

晓得了原型、原型链,那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);            // 能够对照一下,看看二者区分
    原文作者:gentlecoder
    原文地址: https://segmentfault.com/a/1190000018722111
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞