JavaScript 的原型链

更新日期: 2021-06-22阅读: 1.5k标签: 原型

前言

我们都知道,js在ES6以前是没有类的概念的,如果想用面向对象的思想来开发它就要去了解他的继承实现方式。

javascript是一门基于原型继承的语言,这在语言设计之初就已经被确定了的,即使在后来的ES6中加入了类的概念,加入了extends关键字,但仍旧无法改变他作为原型继承的本质,ES6的继承可以看作是ES5继承方式的语法糖,将之前的写好的继承方式合法化,规范化。

什么是原型

我一般喜欢把原型比作模型,或者说模具更加贴切。比如说一个水杯,他是圆的还是方的就是由这个模具决定的,如果这个模具改变了样子,那么生产出来的水杯也会变了样子。

这样来描述,你可能会觉得,叫什么原型,叫构造器不是更贴切。没错,在这个例子中,模具就是水杯的构造器(constructor)。

那么原型和说的这个有什么关系呢。假设我们的这个水杯是有额外功能的,加热和制冷。那么问题就来了,构造器只能决定水杯的样子,是没办法给他提供加热和制冷的功能的。这个时候就需要原型(prototype)了。

一般情况,构造器决定样子,名称等固定的属性。原型决定的是功能,可以进行操作的方法。构造器(constructor)和原型(prototype)共同决定了一个物体的存在形式。

构造器(constructor)和原型(prototype)的关系怎么来描述呢

原型对象的 constructor 是 构造器。

构造器的 prototype 是原型对象。


在js中有一句话叫万物皆对象,每个对象都有原型。我们创建函数,如果采用new的方式调用,当然这种调用方式有个名字叫实例化。

// 创建一个函数
function B(name) {
    this.name = name;
};
// 实例化
var bb = new B('实例化的b');
console.log(bb.name); // 实例化的b;

如上面的代码,bb是通过B实例化之后得到的对象。在这里B就是一个构造器,他所拥有的名字(this.name)属性会带给bb;这也符合之前杯子的例子,杯子的属性会从构造器中获得。

假如我们想要做出来的bb具有一定的功能,那么就需要在原型上下功夫了。根据上面构造器和原型的关系。我们可以这样做。

// 创建一个函数

function B(name) {
    this.name = name;
};

// 在原型上添加一个方法

B.prototype.tan = function() {
    alert('弹出框');
}

// 实例化

var bb = new B('实例化的b');

console.log(bb.name); // 实例化的b;

bb.tan(); // alert('弹出框');

在上面的代码中,我们在B的原型上添加了一个tan的方法,在实例化出来的bb也具备了这个方法。这里我们就简单实现了一个类。用下面一张图,说明一下。实例对象(bb), 原型(prototype), 构造函数(constructor)的关系。


B是我们构造的一个类,这里称为构造函数。他用prototype指向了自己的原型。而他的原型也通过constructor指向了它。

B.prototype.constructor === B;  // true;

bb和B没有直接的关联,虽然B是bb的构造函数,这里用虚线表示。bb有一个__ proto__属性,指向了B的prototype

bb.__ proto__ === B.prototype; // true;
bb.__ proto__.constructor = B; // true;

总之

1,每创建一个函数B,就会为该函数创建一个prototype属性,这个属性指向函数的原型对象;

2,原型对象会默认去取得constructor属性,指向构造函数。

3,当调用构造函数创建一个新实例bb后,该实例的内部将包含一个指针__ proto__,指向构造函数的原型对象。

默认原型

我们知道,所有引用对象都默认继承了Object,所有函数的默认原型都是Object的实例。
之前说过构造函数和原型之间具备对应关系,如下:


既然函数的默认原型都是Object的实例,B的原型对象也应该是Object的实例子,也就是说。B的原型的__ proto__应该指向Objct的原型。


Object的原型对象的原型是最底部了,所以不存在原型,指向NULL;

console.log(Object.prototype.__ proto__); // null;


Function对象

我们知道,函数也是对象,任何函数都可以看作是由构造函数Function实例化的对象,所以Function与其原型对象之间也存在如下关系


如果将Foo函数看作实例对象的话,其构造函数就是Function(),原型对象自然就是Function的原型对象;同样Object函数看作实例对象的话,其构造函数就是Function(),原型对象自然也是Function的原型对象。


如果Function的原型对象看作实例对象的话,如前所述所有对象都可看作是Object的实例化对象,所以Function的原型对象的__ proto __指向Object的原型对象。


到这里prototype,__ proto __, constructor三者之间的关系我们就说完了。

实现继承

function Animal() {
    this.type = '动物';
}
Animate.prototype.eat = function() {
  console.log('吃食物');
}

上面定义了一个动物类,作为父类

function Cat(name) {
    this.name = name || ‘小猫’;
}

定义了一个猫作为子类,这里我们要继承动物类的eat方法和type属性

function Cat(name){
  Animal.call(this);
  this.name = name || '小猫';
}

在实例化Cat时通过call执行了Animal类, 这样Animal中的this就被修改为当前Cat的this。所有的属性也会加在Cat上。

(function(){
  // 创建一个没有实例方法的类
  var Super = function(){};
  Super.prototype = Animal.prototype;
  //将实例作为子类的原型
  Cat.prototype = new Super();
})();

通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化实例方法/属性。而父类的方法仍旧可以赋值给子类。

Cat.prototype = new Super(); 可以实现方法的继承,是因为,根据前面的知识我们知道 new Cat()的__ proto __是指向 Cat的原型的。

(new Cat()).__ proto __ === Cat.prototype; // true

new Cat()所有的方法都是从原型上取到的。

我们通过 Cat.prototype = new Super(); 公式变成了。

(new Cat()).__ proto __ = Cat.prototype = new Super();

所以现在(new Cat()).__ proto __ 指向了 Super的prototype。也就是new Cat的方法是继承自Super.prototype。

Super.prototype又在前一句等于Animal.prototype。所以实现了Cat继承Animal。

这里我们就实现了js属性和方法的继承。不过还在最后一个小问题。

我们知道 prototype 和 constructor 是相互指向的。

Cat.prototype.constructor 应该等于 Cat;

但是随着我们的修改了Cat.prototype = Super.prototype;

现在Cat.prototype.constructor是等于Super的。

所以我们还应该纠正这个问题,一句话搞定。

Cat.prototype.constructor = Cat; // 需要修复下构造函数

以上就是js的原型继承,完整代码如下。

// 创建一个父类
function Animal() {
    this.type = '动物';
}
// 给父类添加一个方法
Animate.prototype.eat = function() {
  console.log('吃食物');
}

// 创建一个子类
function Cat(name){
  // 继承Animal的属性
  Animal.call(this);
  this.name = name || '小猫';
}

// 继承 Animal 的方法

(function(){
  // 创建一个没有实例方法的类
  var Super = function(){};
  Super.prototype = Animal.prototype;
  //将实例作为子类的原型
  Cat.prototype = new Super();
})();
// 修正构造函数
Cat.prototype.constructor = Cat; 

好啦,js的继承原理和prototype,proto, constructor之间的关系我们就说完了,ES6底层的实现方式原理基本相同。

链接: https://fly63.com/article/detial/10567

JS的原型链的理解。

JS是一个非常有魅力的语言也是一个比较烦人的语言,主要就是因为他的特殊性灵活性。JS的原型链,需要深刻的研究才能搞懂。不要纠结细节吧。实在不行就按这个死背住,慢慢就理解了。总之吧就是一句话万物皆对象。

JavaScript 原型的深入指南

不学会怎么处理对象,你在 JavaScript 道路就就走不了多远。它们几乎是 JavaScript 编程语言每个方面的基础。事实上,学习如何创建对象可能是你刚开始学习的第一件事。

JavaScript原型深入浅出

不学会怎么处理对象,你在 JavaScript 道路就就走不了多远。它们几乎是 JavaScript 编程语言每个方面的基础。事实上,学习如何创建对象可能是你刚开始学习的第一件事。

javascript中的prototype和__proto__的理解

在工作中有时候会看到prototype和__proto__这两个属性,对这两个属性我一直比较蒙圈,但是我通过查阅相关资料,决定做一下总结加深自己的理解

理解Js原型和原型链

在JS中,函数的本质就是对象,它与其他对象不同的是,创建它的构造函数与创建其他对象的构造函数不一样。那产生函数对象的构造函数是什么呢?是一个叫做Function的特殊函数,通过new Function 产生的对象就是一个函数。

JS原型解析

JS中原型是为了实现代码重用的一种仿类机制,不过它跟类又完全不同。它通过给对象添加原型关系(即给某个对象添加__proto__属性)实现一个关联。把共有的方法和属性放到这个关联上即实现了JS的继承。简单来说就是一种委托机制

js中__proto__与prototype的关系

了解JavaScript中原型以及原型链只需要记住以下2点即可:对象都有__proto__属性,指向构造函数的prototype;构造函数\\函数都有prototype属性,指向构造函数的原型

js原型链的看法

对象:1,函数对象:有function创造出来的函数2,普通对象:除开函数对象之外的对象,都是普通对象**即普通对象obj是构造函数Object的一个实例,因此:

Js原型到原型链

每个函数都有一个 prototype 属性,函数的 prototype 属性指向了一个对象,这个对象正是调用该构造函数而创建的实例的原型,也就是这个例子中的 cat 和dog 的原型。

Js原型对象

这里不介绍原型链。javascript 中有若干长得跟prototype,proto很想的属性/函数,这里简单总结一下他们都是啥,哪个是原型对象,哪个不是。[[Prototype]]这个对象的一个内置槽,对程序员是不可见

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!