面试题之原型、原型链、和继承

似乎生活中常常会遇到这种情况,你去一家公司面试,前面面的都挺好,你觉得你对基础算法的了解很好,各种排序,红黑树,二叉树,深度/广度优先算法都答出来了,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);	// 可以对比一下,看看两者区别
复制代码

转载于:https://juejin.im/post/5ca0e259e51d4556dd6934c8

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值