理解 javascript 的原型链及继承

来源:http://www.chinese-glasses.com 作者:Web前端 人气:178 发布时间:2020-05-06
摘要:在javascript中,函数可以有属性。每个函数都有一个特殊的属性叫作原型(prototype) 理解 javascript 的原型链及继承 class Shape{}class Circle extends Shape{}const circle = new Circle();console.log(circle.__pr

在javascript中,函数可以有属性。 每个函数都有一个特殊的属性叫作原型(prototype)

理解 javascript 的原型链及继承

class Shape{}
class Circle extends Shape{}
const circle = new Circle();

console.log(circle.__proto__ === Circle.prototype);
console.log(Circle.prototype.__proto__ === Shape.prototype);
console.log(Shape.prototype.__proto__ === Object.prototype);
console.log(Object.prototype.__proto__ === null);

console.log(Circle.__proto__ === Shape);
console.log(Shape.__proto__ === Function.prototype);
console.log(Function.prototype.__proto__ === Object.prototype);
console.log(Object.prototype.__proto__ === null);

console.log(circle.constructor === Circle)

以上所有的运行结果都是 true;

10bet 1

我们发现,person可以调用其构造函数原型里的say方法,why?让我们看下 person 里都有什么:

prototype(显式原型)

不像每个对象都有proto属性来标识自己所继承的原型,只有函数才有prototype属性。

JS不像其它面向对象的语言,它没有类(class,ES6引进了这个关键字,但更多是语法糖)的概念。JS通过函数来模拟类。

当你创建函数时,JS会为这个函数自动添加prototype属性,值是空对象。而一旦你把这个函数当作构造函数(constructor)调用(即通过new关键字调用),那么JS就会帮你创建该构造函数的实例,实例继承构造函数prototype的所有属性和方法(实例通过设置自己的proto指向承构造函数的prototype来实现这种继承)。

JS是单继承的,Object.prototype是原型链的顶端,所有对象从它继承了包括toString等等方法和属性。Object.prototype.10bet,proto === null,说明原型链到Object.prototype终止。

这里可能有的同学对Object.create()这个函数不太熟悉,不明白其做了什么,不要急,让我们来看下其源码实现:

3、所有的原型对象都有constructor属性,该属性对应创建所有指向该原型的实例的构造函数;

现在我们知道了原型是一个对象,那原型对象有什么作用呢?实际上,原型是 ECMAScript 实现继承的过程中产生的一个概念。这里我们简单拿 ES5 的对象来举例子:

Object 对象的属性

属性 描述
constructor 返回创建该对象的构造函数。
prototype 返回创建该对象的函数的原型对象。

一,原型

总结:

  • 对象有属性proto,指向该对象的构造函数的原型对象。
  • 方法除了有属性proto,还有属性prototype,prototype指向该方法的原型对象。
parent.__proto__ ==Parent.prototype// true

extends

用关键词extends声明子类关系:

    class Circle extends Shape {}

extends后面可以接驳任意合法且拥有prototype属性的构造函数。它可以是:

另一个类
源自现有继承框架(译者注:作者指的是原型继承,即使在JavaScript中类继承的本质也是原型继承)的近类函数
一个普通的函数
一个包含一个函数或类的变量
一个对象上的属性访问
一个函数调用

原型与原型链一直是学习JS绕不过的知识点,其中protoprototype最为让人头疼,这里简单的写下我自己的理解,从原型与原型链中拆解protoprototype,希望能对大家有所帮助。

4、函数对象和原型对象通过prototype和constructor属性进行相互关联。
1,child对象里没有 print 函数,于是便在其原型上寻找:child.__proto__ —— f.prototype2,进入 f.prototype 中寻找 print 函数,发现没有,于是去其原型上寻找:f.__proto__ —— F.prototype3,F.prototype == Parent.prototype,于是便进入 Parent.prototype 中寻找 print 函数,有 print 函数,调用成功

例一

//函数声明创一个空函数
function foo(){}

10bet 2

3229842-48de74a3cfec7e9d

foo这个函数就有个prototype属性,并且它默认有两个属性:constructor和proto。constructor属性会指向它本身foo,proto是在chrome中暴露的(不是一个标准属性),那么foo.prototype的原型会指向Object.prototype。因此Object.prototype上的一些方法toString,valueOf才会被每个一般的对象所使用。

咦,有没有发现 parent.__proto__ 与 Parent.prototype所指向的对象很相似,它们知否是同一个对象呢?

三种构造对象的方法:

  1. 通过对象字面量构造。
var person1 = {
    name: 'cyl',
    sex: 'male'
};

形如这个形式的叫做对象字面量。这样子构造出的对象,其[[prototype]]指向Object.prototype

  1. 构造函数构造。
function Person(){}
var person1 = new Person();

通过new操作符调用的函数就是构造函数。由构造函数构造的对象,其[[prototype]]指向其构造函数的prototype属性指向的对象。每个函数都有一个prototype属性,其所指向的对象带有constructor属性,这一属性指向函数自身。(在本例中,person1的[[prototype]]指向Person.prototype)

  1. 函数Object.create构造的。
var person1 = {
    name: 'cyl',
    sex: 'male'
};

var person2 = Object.create(person1);

本例中,对象person2的[[prototype]]指向对象person1。在没有Object.create函数的日子里,人们是这样做的:

Object.create = function(p) {
    function f(){}
    f.prototype = p;
    return new f();
}
class Parent { constructor(name) { this.name = name; } print() { console.log(this.name); }}let parent = new Parent('小明');console.dir(parent);console.dir(Parent);

例三

// 声明构造函数
function Person(name, age) {
    this.name = name;
    this.age = age;
}

// 通过prototye属性,将方法放到原型对象上
Person.prototype.getName = function() {
    return this.name;
}

var p1 = new Person('tim', 10);
var p2 = new Person('jak', 22);

10bet 3

3229842-3e7187f42cdfcda9

构造函数的prototype与所有实例对象的proto都指向原型对象。而原型对象的constructor指向构造函数。

对一个知识点是否完全把握,最好的校验方法就是能否用自己的语言将其表述出来。

1、所有的对象都有 proto 属性,该属性对应该对象的原型;
let obj = {a: 1};console.log(obj);

图示

10bet 4

  1. 在JS里,万物皆对象。方法(Function)是对象,方法的原型(Function.prototype)是对象。因此,它们都会具有对象共有的特点。即:对象具有属性proto,可称为隐式原型,一个对象的隐式原型指向构造该对象的构造函数的原型,这也保证了实例能够访问在构造函数原型中定义的属性和方法。
  2. 方法(Function)方法这个特殊的对象,除了和其他对象一样有上述proto属性之外,还有自己特有的属性——原型属性(prototype),这个属性是一个指针,指向一个对象,这个对象的用途就是包含所有实例共享的属性和方法(我们把这个对象叫做原型对象)。原型对象也有一个属性,叫做constructor,这个属性包含了一个指针,指回原构造函数。

1,定义

作用:

隐式原型的作用:构成原型链,同样用于实现基于原型的继承。举个例子,当我们访问obj这个对象中的x属性时,如果在obj中找不到,那么就会沿着proto依次查找。

看不懂定义没关系,让我们举例说明:

二、原型链

每一个构造函数都有一个原型对象,当我们让某一原型对象等于另一构造函数的实例,此时该原型对象就包含一个指针,该指针指向这一构造函数的原型对象,该指针指向的原型对象中包含一个指向这一构造函数的指针,同样我们可以令该指针指向的原型对象等于另一构造函数的实例,如此递进,则形成一条实例与原型的链条,即原型链。

 function Parent() {
        this.name = 'parent';
    };
    Parent.prototype.getName = function() {
        return this.name;
    };
    function Child() {
        this.childname = 'child';
    }
    Child.prototype = new Parent();
    Child.prototype.getChildName = function() {
        return this.childname;
    };
    var child = new Child();
    console.log(child.getName()); //输出parent
    console.log(child.getChildName());  //输出child

在上面的例子中,child实例指向Child原型,Child原型等于Parent实例,即指向Parent原型。可见本质即是以一个构造函数的实例重写原型对象,形成原型链。

总结

解题思路如下:

一、原型

2,使用

2、所有的函数对象都有 prototype 属性,该属性的值会被赋值给该函数创建的对象的 proto属性;

Object.prototype 的proto属性是一个访问器属性(一个getter函数和一个setter函数), 暴露了通过它访问的对象的内部[[Prototype]] (一个对象或 null)。

上图解析

1.构造函数Foo()构造函数的原型属性Foo.prototype指向了原型对象,在原型对象里有共有的方法,所有构造函数声明的实例(这里是f1,f2)都可以共享这个方法。
2.原型对象Foo.prototypeFoo.prototype保存着实例共享的方法,有一个指针constructor指回构造函数。
3.实例f1和f2是Foo这个对象的两个实例,这两个对象也有属性proto,指向构造函数的原型对象,这样子就可以像上面1所说的访问原型对象的所有方法啦。另外:构造函数Foo()除了是方法,也是对象啊,它也有proto属性,指向谁呢?指向它的构造函数的原型对象呗。函数的构造函数不就是Function嘛,因此这里的proto指向了Function.prototype。其实除了Foo(),Function(), Object()也是一样的道理。原型对象也是对象啊,它的proto属性,又指向谁呢?同理,指向它的构造函数的原型对象呗。这里是Object.prototype.最后,Object.prototype的proto属性指向null。

实例 person 虽然自身没有 say 方法,但是通过proto属性访问到了其原型中的 say 方法。

例二

function foo(){}
foo.prototype.x = 1;
var obj3 = new foo();//实例对象
console.log(obj3.x); //1

我们这里有个foo函数,这个函数有个prototype的对象属性,它的作用就是当使用new foo()去构造实例的时候,这个构造器的prototype属性会用作new出来的这些对象的原型。

在这个例子中,obj3.proto===foo.prototype,即,每个对象都有一个proto属性,指向创建该对象的函数的prototype。Object.prototype是一个特例,它的proto指向的是null。

结果是同一个引用,这个时候我们可以得出一个结论:实例的__proto__属性指向其构造函数的原型。那他们之间的这种关联关系有什么作用呢?这便涉及到了原型链。

__proto__(隐式原型)

每个JS对象一定对应一个原型对象,并从原型对象继承属性和方法。

对象proto属性的值就是它所对应的原型对象,隐式原型指向创建这个对象的函数(constructor)的prototype。

JavaScript中任意对象都有一个内置属性[[prototype]],在ES5之前没有标准的方法访问这个内置属性,但是大多数浏览器都支持通过proto来访问。ES5中有了对于这个内置属性标准的Get方法Object.getPrototypeOf().
Object.prototype 这个对象是个例外,它的proto值为null

var one = {x: 1};
var two = new Object();
console.log(one.__proto__ === Object.prototype)//true
console.log(two.__proto__ === Object.prototype)//true

原型(prototype)是什么东西呢,里面又有哪些属性呢?来,让我们拿个具体例子看下:

作用:

显式原型的作用:用来实现基于原型的继承与属性的共享。


Object本身是构造函数,继承了Function.prototype;Function也是对象,继承了Object.prototype。

Object instanceof Function // true
Function instanceof Object // true

instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的prototype 属性。

ES规范是怎么说的?

Function本身就是函数,Function.proto是标准的内置对象Function.prototype。
Function.prototype.proto是标准的内置对象Object.prototype。

时间: 2019-09-08阅读: 87标签: prototype一,前言

声明了一个构造函数F,然后将其原型指向参数proto,返回构造函数的实例。好,让我们将整个过程串起来看一下:

JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。

我们可以看出proto指向了一个对象,这个对象是什么呢?来,让我们继续看

声明了一个构造函数 Person,其有一个属性 name在其原型上声明了一个函数 say实例一个 Person 类——person调用 person 的 say 方法

本文由10bet发布于Web前端,转载请注明出处:理解 javascript 的原型链及继承

关键词:

上一篇:iOS常用数据持久化

下一篇:没有了

最火资讯