关于javascript:这可能是掘金讲原型链讲的最好最通俗易懂的了附练习题

31次阅读

共计 6360 个字符,预计需要花费 16 分钟才能阅读完成。

前言

大家好,我是林三心,置信大家都听过前端的三座大山:闭包,原型链,作用域 ,这三个其实都只是算根底。而我始终感觉 根底是进阶的前提 ,所以不能因为是根底就漠视他们。明天我就以我的形式讲讲 原型链 吧,心愿大家能牢固地把握 原型链 常识

很多文章一上来就扔这个图,然而我不喜爱这样,我感觉这样对根底不好的同学很不好,我喜爱率领大家去从零实现这个图,在实现的过程中,一直地把握原型链的所有常识!!!来吧!!!跟着我从零实现吧!!!跟着我驯服原型链吧!!!

prototype 和__proto__

是啥

这两个货色到底是啥呢?

  • prototype: 显式原型
  • __ proto__: 隐式原型

有什么关系

那么这两个都叫原型,那他们两到底啥关系呢?

个别,构造函数 的 prototype 和其 实例 的__proto__是指向同一个中央的,这个中央就叫做 原型对象

那什么是构造函数呢?俗话说就是,能够用来 new 的函数就叫构造函数,箭头函数不能用来当做构造函数哦

function Person(name, age) { // 这个就是构造函数
  this.name = name
  this.age = age
}

const person1 = new Person('小明', 20) // 这个是 Person 构造函数的实例
const person2 = new Person('小红', 30) // 这个也是 Person 构造函数的实例

构造函数 的 prototype 和其 实例 的__proto__是指向同一个中央的,咱们能够来验证一下

function Person(name, age) {
  this.name = name
  this.age = age
}
Person.prototype.sayName = function() {console.log(this.name)
}
console.log(Person.prototype) // {sayName: [Function] }

const person1 = new Person('小明', 20)
console.log(person1.__proto__) // {sayName: [Function] }

const person2 = new Person('小红', 30)
console.log(person2.__proto__) // {sayName: [Function] }

console.log(Person.prototype === person1.__proto__) // true
console.log(Person.prototype === person2.__proto__) // true

函数

咱们下面提到了 构造函数,其实他说到底也是个函数,其实咱们平时定义函数,无非有以下几种

function fn1(name, age) {console.log(` 我是 ${name}, 我往年 ${age}岁 `)
}
fn1('林三心', 10) // 我是林三心, 我往年 10 岁

const fn2 = function(name, age){console.log(` 我是 ${name}, 我往年 ${age}岁 `)
}
fn2('林三心', 10) // 我是林三心, 我往年 10 岁

const arrowFn = (name, age) => {console.log(` 我是 ${name}, 我往年 ${age}岁 `)
}
arrowFn('林三心', 10) // 我是林三心, 我往年 10 岁

其实这几种的实质都是一样的 (只思考函数的申明),都能够应用new Function 来申明,是的没错 Function 也是一个构造函数。下面的写法等同于上面的写法

const fn1 = new Function('name', 'age', 'console.log(` 我是 ${name}, 我往年 ${age}岁 `)')
fn1('林三心', 10) // 我是林三心, 我往年 10 岁

const fn2 = new Function('name', 'age', 'console.log(` 我是 ${name}, 我往年 ${age}岁 `)')
fn2('林三心', 10) // 我是林三心, 我往年 10 岁

const arrowFn = new Function('name', 'age', 'console.log(` 我是 ${name}, 我往年 ${age}岁 `)')
arrowFn('林三心', 10) // 我是林三心, 我往年 10 岁

咱们之前说过,构造函数 prototype和其 实例 __proto__是指向同一个中央的,这里的 fn1,fn2,arrowFn 其实也都是 Function 构造函数 的实例,那咱们来验证一下吧

function fn1(name, age) {console.log(` 我是 ${name}, 我往年 ${age}岁 `)
}

const fn2 = function(name, age){console.log(` 我是 ${name}, 我往年 ${age}岁 `)
}

const arrowFn = (name, age) => {console.log(` 我是 ${name}, 我往年 ${age}岁 `)
}

console.log(Function.prototype === fn1.__proto__) // true
console.log(Function.prototype === fn2.__proto__) // true
console.log(Function.prototype === arrowFn.__proto__) // true

对象

咱们平时开发中,创立一个对象,通常会用以下几种办法。

  • 构造函数创建对象 ,他创立进去的对象都是此Function 构造函数 的实例,所以这里不探讨它
  • 字面量创建对象
  • new Object 创建对象
  • Object.create 创建对象,创立进去的是一个空原型的对象,这里不探讨它

    // 第一种:构造函数创建对象
    function Person(name, age) {
    this.name = name
    this.age = age
    }
    const person1 = new Person('林三心', 10)
    console.log(person1) // Person {name: '林三心', age: 10}
    
    // 第二种:字面量创建对象
    const person2 = {name: '林三心', age: 10}
    console.log(person2) // {name: '林三心', age: 10}
    
    // 第三种:new Object 创建对象
    const person3 = new Object()
    person3.name = '林三心'
    person3.age = 10
    console.log(person3) // {name: '林三心', age: 10}
    
    // 第四种:Object.create 创建对象
    const person4 = Object.create({})
    person4.name = '林三心'
    person4.age = 10
    console.log(person4) // {name: '林三心', age: 10}

    咱们来看看 字面量创建对象 new Object 创建对象 两种形式,其实 字面量创建对象 的实质就是new Object 创建对象

    // 字面量创建对象
    const person2 = {name: '林三心', age: 10}
    console.log(person2) // {name: '林三心', age: 10}
    
    实质是
    
    // new Object 创建对象
    const person2 = new Object()
    person2.name = '林三心'
    person2.age = 10
    console.log(person2) // {name: '林三心', age: 10}

咱们之前说过,构造函数 prototype和其 实例 __proto__是指向同一个中央的,这里的 person2,person3 其实也都是 Object 构造函数 的实例,那咱们来验证一下吧

const person2 = {name: '林三心', age: 10}

const person3 = new Object()
person3.name = '林三心'
person3.age = 10

console.log(Object.prototype === person2.__proto__) // true
console.log(Object.prototype === person3.__proto__) // true

Function 和 Object

下面咱们常说

  • 函数 Function 构造函数 的实例
  • 对象 Object 构造函数 的实例

Function 构造函数Object 构造函数 他们两个又是谁的实例呢?

  • function Object()其实也是个函数,所以他是 Function 构造函数 的实例
  • function Function()其实也是个函数,所以他也是 Function 构造函数 的实例,没错,他是他本人自身的实例

咱们能够试验一下就晓得了

console.log(Function.prototype === Object.__proto__) // true
console.log(Function.prototype === Function.__proto__) // true

constructor

constructor 和 prototype 是成对的,你指向我,我指向你。举个例子,如果你是我老婆,那我必定是你的老公。

function fn() {}

console.log(fn.prototype) // {constructor: fn}
console.log(fn.prototype.constructor === fn) // true

原型链

Person.prototype 和 Function.prototype

探讨原型链之前,咱们先来聊聊这两个货色

  • Person.prototype,它是 构造函数 Person的原型对象
  • Function.prototype,他是 构造函数 Function的原型对象

都说了原型对象,原型对象,能够晓得其实这两个实质都是 对象

那既然是 对象 ,实质必定都是通过new Object() 来创立的。既然是通过 new Object() 创立的,那就阐明 Person.prototype 和 Function.prototype 都是 构造函数 Object的实例。也就阐明了 Person.prototype 和 Function.prototype 他们两的 __proto__ 都指向Object.prototype

咱们能够验证一下

function Person(){}

console.log(Person.prototype.__proto__ === Object.prototype) // true
console.log(Function.prototype.__proto__ === Object.prototype) // true

什么是原型链?

什么是原型链呢?其实俗话说就是:__proto__的门路 就叫原型链

原型链起点

下面咱们看到,三条原型链结尾都是 Object.prototype,那是不是阐明了Object.prototype 就是原型链的起点呢?其实不是的,Object.prototype其实也有__proto__,指向 null,那才是原型链的起点

至此,整个原型示意图就画完啦!!!

原型继承

说到原型,就不得不说补充一下 原型继承 这个知识点了,原型继承 就是,实例 能够应用 构造函数上的 prototype中的办法

function Person(name) { // 构造函数
  this.name = name
}
Person.prototype.sayName = function() { // 往原型对象增加办法
  console.log(this.name)
}


const person = new Person('林三心') // 实例
// 应用构造函数的 prototype 中的办法
person.sayName() // 林三心

instanceof

应用办法

A instanceof B

作用:判断 B 的 prototype 是否在 A 的原型链上

例子

function Person(name) { // 构造函数
  this.name = name
}

const person = new Person('林三心') // 实例

console.log(Person instanceof Function) // true
console.log(Person instanceof Object) // true
console.log(person instanceof Person) // true
console.log(person instanceof Object) // true

练习题

练习题只为了大家能坚固本文章的常识

第一题

var F = function() {};

Object.prototype.a = function() {console.log('a');
};

Function.prototype.b = function() {console.log('b');
}

var f = new F();

f.a();
f.b();

F.a();
F.b();

答案

f.a(); // a
f.b(); // f.b is not a function

F.a(); // a
F.b(); // b

第二题

var A = function() {};
A.prototype.n = 1;
var b = new A();
A.prototype = {
  n: 2,
  m: 3
}
var c = new A();

console.log(b.n);
console.log(b.m);

console.log(c.n);
console.log(c.m);

答案

console.log(b.n); // 1
console.log(b.m); // undefined

console.log(c.n); // 2
console.log(c.m); // 3

第三题

var foo = {},
    F = function(){};
Object.prototype.a = 'value a';
Function.prototype.b = 'value b';

console.log(foo.a);
console.log(foo.b);

console.log(F.a);
console.log(F.b);

答案

console.log(foo.a); // value a
console.log(foo.b); // undefined

console.log(F.a); // value a
console.log(F.b); // value b

第四题

function A() {}
function B(a) {this.a = a;}
function C(a) {if (a) {this.a = a;}
}
A.prototype.a = 1;
B.prototype.a = 1;
C.prototype.a = 1;

console.log(new A().a); 
console.log(new B().a);
console.log(new C(2).a);

答案

console.log(new A().a); // 1
console.log(new B().a); // undefined
console.log(new C(2).a); // 2

第五题

console.log(123['toString'].length + 123)

答案:123 是数字,数字实质是 new Number(),数字自身没有toString 办法,则沿着 __proto__function Number()prototype 上找,找到 toString 办法,toString 办法的 length 是 1,1 + 123 = 124,至于为什么 length 是 1,能够看 95% 的人都答复不上来的问题:函数的 length 是多少?

console.log(123['toString'].length + 123) // 124

结语

如果你感觉此文对你有一丁点帮忙,点个赞,激励一下林三心哈哈。或者退出我的群哈哈,咱们一起摸鱼一起学习

正文完
 0