温故js系列(15)-原型&原型链&原型继承

前端学习:教程&开发模块化/规范化/工程化/优化&工具/调试&值得关注的博客/Git&面试-前端资源汇总前端

欢迎提issues斧正:原型&原型链&原型继承git

JavaScript-原型&原型链&原型继承

JavaScript的原型是一个重要的知识点,不少扩展应用都是从原型出发的。要说原型,咱们先简单说一下函数建立过程。上一篇文章用闭包实现类和继承中用的是原型继承,今天就讲一讲原型继承。更多继承在后面的文章中更新。github

函数建立过程

function Xzavier() {};

1.建立一个对象(有constructor属性及[[Prototype]]属性),其中[[Prototype]]属性不可访问、不可枚举。
2.建立一个函数(有name、prototype属性),再经过prototype属性 引用第1步建立的对象。
3.建立变量Xzavier,同时把函数的引用赋值给变量Xzavier。面试

构造函数

构造函数是用来新建同时初始化一个新对象的函数,因此,任何一个函数均可以是构造函数。只是咱们在写代码的时候通常首字母大写以便区分使用。闭包

原型

每一个函数在建立的时候js都自动添加了prototype属性,这就是函数的原型,原型就是函数的一个属性,相似一个指针。原型在函数的建立过程当中由js编译器自动添加。模块化

function Xzavier() {
    this.name = 'xzavier';
    this.sex = 'boy';
    this.job = "jser";
}
//给A添加属性
Xzavier.age = 23;
//给A的原型对象添加属性
Xzavier.prototype.sports = function() {console.log('basketball')}
Xzavier.prototype = {
    hobbit1: function() {console.log('basketball');},
    hobbit2: function() {console.log('running');}
};

原型链

在JavaScript中,每一个对象都有一个[[Prototype]]属性,其保存着的地址就构成了对象的原型链
[[Prototype]]属性是js编译器在对象被建立时自动添加的,其取值由new运算符的右侧参数决定。字面量的方式可转化为new Obejct();函数

var x = new Xzavier();
vae o = {};  //var o = new Obejct();

经过对象的[[Prototype]]保存对另外一个对象的引用,经过这个引用往上进行属性的查找,这就是原型链查找机制工具

对象在查找某个属性的时候,会首先遍历自身的属性,若是没有则会继续查找[[Prototype]]引用的对象,若是再没有则继续查找[[Prototype]].[[Prototype]]引用的对象,依次类推,直到[[Prototype]].….[[Prototype]]undefined学习

var str = new String('xzavier');
str

图片描述

Object.prototype[[Prototype]]就是undefined优化

function Xzavier() {
    this.name = 'xzavier';
}
var x = new Xzavier();
x.age = 23;

console.log(x.job);  // 获取x的job属性 undefined

一、遍历x对象自己,结果x对象自己没有job属性
二、找到x的[[Prototype]],也就是其对应的对象Xzavier.prototype,同时进行遍历。 Xzavier.prototype也没有job属性
三、找到Xzavier.prototype对象的[[Prototype]],指向其对应的对象Object.prototype。Object.prototype也没有job属性
四、寻找Object.prototype的[[Prototype]]属性,返回undefined。

函数的变量和内部函数

说了函数的原型链,这里须要说一下的变量和内部函数。

私有变量和内部函数

私有成员,即定义函数内部的变量或函数,外部没法访问。

function Xzavier(){
    var name = "xzavier"; //私有变量
    var sports = function() {console.log('basketball')}; //私有函数 
}
var x = new Xzavier();
x.name;  //undefined

若是要访问,须要对外提供接口。

function Xzavier(){
    var name = "xzavier"; //私有变量
    var sports = function() {console.log('basketball')}; //私有函数
    return{
        name: name,
        sports: sports
    }
}
var x = new Xzavier();
x.name;  //"xzavier"

静态变量和内部函数

用点操做符定义的静态变量和内部函数就是实例不能访问到的变量和内部函数。只能经过自身去访问。

function Xzavier(){
    Xzavier.name = "xzavier"; //静态变量
    Xzavier.sports = function() {console.log('basketball')}; //静态函数 
}
Xzavier.name; //"xzavier"
var x = new Xzavier();
x.name;  //undefined

实例变量和内部函数

经过this定义给实例使用的属性和方法。

function Xzavier(){
    this.name = "xzavier"; //实例变量
    this.sports = function() {console.log('basketball');}; //实例函数 
}
Xzavier.name; //undefined
var x = new Xzavier();
x.name;  //"xzavier"

原型链继承

有了原型链,就能够借助原型链实现继承。

function Xzavier() {
    this.name = 'xzavier';
    this.sex = 'boy';
    this.job = "jser";
}

function X() {};

X的原型X.prototype原型自己就是一个Object对象。F12打开控制台输入函数,再打印X.prototype:

Object {
    constructor: X()
    __proto__: Object
}

prototype自己是一个Object对象的实例,因此其原型链指向的是Object的原型。

X.prototype = Xzavier.prototype

X.prototype = Xzavier.prototype;

这样至关于把X的prototype指向了Xzavier的prototype;
这样只是继承了Xzavier的prototype方法,Xzavier中的自定义方法则不继承。

X.prototype.love = "dog";

这样也会改变Xzavier的prototype,因此这样基础就很差。

X.prototype = new Xzavier()

X.prototype = new Xzavier();

这样产生一个Xzavier的实例,同时赋值给X的原型,也即X.prototype至关于对象:

{
    name: "xzavier", 
    sex: "boy", 
    job: "jser",
    [[Prototype]] : Xzavier.prototype
}

这样就把Xzavier的原型经过X.prototype.[[Prototype]]这个对象属性保存起来,构成了原型的连接。
不过,这样X产生的对象的构造函数发生了改变,由于在X中没有constructor属性,只能从原型链找到Xzavier.prototype,读出constructor:Xzavier。

var x = new X;
console.log(x.constructor);

输出:
Xzavier() {
    this.name = 'xzavier';
    this.sex = 'boy';
    this.job = "jser";
}

手动改正:

X.prototype.constructor = X;

这是X的原型就多了个属性constructor,指向X。这样就OK。

function Xzavier() {
    this.name = 'xzavier';
    this.sex = 'boy';
    this.job = "jser";
}

function X(){}
X.prototype = new Xzavier();
var x = new X()
x.name  // "xzavier"

[[Prototype]],__proto__,prototype

关于咱们常常遇到的[[Prototype]],__proto__,prototype

咱们在控制台打印 var str = new String('xzavier'),展开查看属性时,只会看到__proto__,因此起做用的是__proto____proto__是对象的内置属性,是每一个对象都有的属性,可是这个属性使用不标准,因此不建议直接使用。可是,咱们的原型链就是基于 __proto__的。经过构造函数获得的实例的 __proto__ 属性,指向其对应的原型对象 String.prototype,这正如文中咱们打印 var str = new String('xzavier') 中看到的同样。

[[Prototype]]是一个隐藏属性,指向的是这个对象的原型。几乎每一个对象有一个[[prototype]]属性。

prototype是每一个函数对象都具备的属性,指向原型对象,若是原型对象被添加属性和方法,那么由应的构造函数建立的实例会继承prototype上的属性和方法,这也是咱们在代码中常常遇到的。构造函数产生实例时,实例经过其对应原型对象的 constructor 访问对应的构造函数对象。因此,咱们继承出来的实例每每没有constructor,只是经过原型链查找,会让咱们产生错觉,可参见本系列原型链文章。

hasOwnProperty

hasOwnProperty是Object.prototype的一个方法,判断一个对象是否包含自定义属性而不是原型链上的属性。
hasOwnProperty 是JavaScript中惟一一个处理属性可是不查找原型链的函数。

function Xzavier() {
    this.name = 'xzavier';
    this.sex = 'boy';
    this.job = "jser";
}
//给A的原型对象添加属性
Xzavier.prototype.sports = function() {console.log('basketball');};

var x = new Xzavier();
x.name; // 'xzavier'
'sex' in x; // true

x.hasOwnProperty('job'); // true
x.hasOwnProperty('sports'); // false

当检查对象上某个属性是否存在时,hasOwnProperty 是很是推荐的方法。

继承在js中使用频繁。ES6也设计了专门的CLASS语法糖供开发者使用。
更多继承方法在新的文章中更新...

可贵周末,应该运动O(∩_∩)O~ 打打篮球,运动运动,有代码,有篮球,有生活。。。长时间不动肩膀(其实身体各地方都是),还真疼啊。但愿程序猿们都健健康康的!!!

相关文章
相关标签/搜索