给我的AI同事简单地讲解了原型链

前言

今天我的同事来问我,XX哥,这个原型链是什么东西?
“哈? 这都不知道,你搞什么前端??”
“我本来就不是前端,我搞AI的”
“哦对哦,那我错怪你了,行吧 那今天我给你说得简单点(毕竟我只是简单地理解了)”

作为Javascript的基础之一,原型一直贯穿我们的JS代码并且成为面试的常考问题。用我自己的话去理解,原型就是类,通过创建(new)一个实例的方法去继承这个类的属性跟方法,而原型链就是一个不断继承的链条,到了顶级(Object)会用null去终止这个链条的延伸。

原型

什么是原型

The prototype is an object that is associated with every functions and objects by default in JavaScript, where function’s prototype property is accessible and modifiable and object’s prototype property (aka attribute) is not visible. Every function includes prototype object by default

用官方的解释就是

在JavaScript中,prototype对象的继承性是实现面向对象的一个重要机制。

每个函数就是一个对象,函数对象都有一个子对象 prototype对象,类是以函数的形式来定义的。prototype表示该函数的原型,也表示一个类的成员的集合

用java的类概念去理解

初学的时候,网上不少人用一张错综复杂的图去诠释原型链的关系,然而对于涉猎未深的同学,这无疑是灾难般的入门方法。而我期望各位先把概念理清,再用一张最基础的图循序渐进地理解原型链的关系。

学过面向对象的同学都会用类去定义一个通用的类型,如此一来,我们创建一个新的实例,便继承了类(Kitten)的方法

public class Kitten{
    int kittenAge;

    public kitten(age){
      kittenAge = age;
    }

    public void roar() {
      System.out.println("喵喵喵"); 
    }
}

在这里直接创建了一个实例(example),可以看到实例继承到了父类(Kitten)的属性跟方法

Kitten example = new Kitten(12);
System.out.println(example.kittenAge);  // 12
example.roar(); // 喵喵喵

用函数实现构造函数

Javascript并没有class这种关键字,虽然我是用class去理解构造函数,但实际上Javascript是用函数去实现

补充:ES后补充了class的写法,但这里只用function做栗子

function Kitten(age){
    this.kittenAge = age
    this.say = function(){
        console.log('喵喵喵')
    }
}

const example = new Kitten(33)
console.log(example.kittenAge) // 33

example 就是构造函数(Kitten)的实例,它继承了Kitten的属性跟方法

理解prototype跟_proto_,constructor

先了解几个规则:

  1. 引用类型,都有一个隐性原型( __proto__)的属性,指向一个普通的对象
  2. 引用类型的隐性原型指向其构造函数的显性原型(prototype)
  3. 在获取对象的属性时,如果它内部没有这个属性,它会去隐性原型去寻找(也就是其构造函数的显性原型)

prototype的中文翻译就是原型,一般称之为显性原型,这个属性只是添加给构造函数用,可以通过prototype去增加属性或方法,实例没有显性原型这个属性.
而_proto_被称为隐性原型,所有引用类型都有隐形原型

要充分了解原型链,我们要特别留意上述的规则2:

function Kitten(age){
    this.kittenAge = age
    this.say = function(){
        console.log('喵喵喵')
    }
}
const example = new Kitten(33)
const obj = {};

example.__proto__ === Kitten.prototype
obj.__proto__ === Object.prototype

以及规则3:obj没有toString()的方法,它会沿着构造函数Object的显性原型去寻找

const obj = {a : 1}
obj.toString() // [object Object]

constructor

这个单词的直译就是构造函数,上面我们用prototype指向构造函数的原型,那么这里就可以用constructor去指向原型的构造函数,:

function Kitten(age){
    this.kittenAge = age
    this.say = function(){
        console.log('喵喵喵')
    }
}
const example = new Kitten(33)
Kitten.prototype.constructor === Kitten // true

用一张最直观的图来显示三者的关系
来源于juejin.cn/post/684490…

给我的AI同事简单地讲解了原型链

必须留意的是,最后的Object的prototype的隐形原型__proto__指向null,是为避免出现原型链的死循环

理解instanceof

想起我刚入行时面试官问我的一个问题

“用什么方法可以判断数据类型?”

“typeof”

“那可以判断出普通对象(Object)跟数组(Array)吗?”

“可以”

结果就是面试官留下了冷冷的不屑,然后我一结束面试马上check了一下。typeof只能判断基本类型,但不能精确判断引用类型。这时候instanceof就发挥作用了,而它就是利用原型链的原理,一直往上寻找,当链上出现xxx.prototype(如Object.prototype)时,就会判断为true

代码举例

const person = new Person()
const arr = [1,2,3]

console.log(person instanceof Person) // true
console.log(arr instanceof Array) // true
console.log(Person instanceof Object) // true
console.log(person instanceof Object) // true

上面用了四个instanceof方法去判断对象跟数组

  1. 根据原型链Person.prototype === person.__proto__,结果为true
  2. Array.prototype === arr.__proto__,结果为true
  3. Object.prototype === Person.__proto__,结果为true
  4. Object.prototype === person.__proto__.__proto__ 沿着原型链一直向上寻找,找到Object.prototype就判断为true

除此之外,Set,Map,Sympol等新加入的ES数据类型也能用原型链instanceof的方法去判断。

手写instanceof

好了,了解清楚之后,写一个笔试常见JS

function instanceof(target, class) {
  // 参数检查
  if(!target || !class || !target.__proto__ || !class.prototype){
    return false;
  }

  let current = target;

  while(current) {   // 一直往原型链上面找
    if(current.__proto__ === class.prototype) {
      return true;    // 找到了返回true
    }

    current = current.__proto__;
  }

  return false;     // 没找到返回false
}


function Parent() {}
function Child() {}

Child.prototype.__proto__ = Parent.prototype;

const obj = new Child();
console.log(myInstanceof(obj, Child) );   // true
console.log(myInstanceof(obj, Parent) );   // true
console.log(myInstanceof({}, Parent) );   // false

总结

  1. 搞清楚原型,实例,构造函数的关系
  2. 理解Instanceof的原理跟用法
  3. 明白Object.prototype.__proto__ === null 是为了杜绝原型链死循环的设定
  4. 通过原型链 清晰理解子类继承父类的原理,如图所示,新建一个对象实例时,就能继承父类的属性跟方法。

给我的AI同事简单地讲解了原型链

终于我的同事满意地走了,不知道下次他会问什么呢

原文链接:https://juejin.cn/post/7215213377094500408 作者:广州交租公

(0)
上一篇 2023年3月29日 下午4:17
下一篇 2023年3月29日 下午4:28

相关推荐

发表回复

登录后才能评论