原型链与继承

一、普通对象与函数对象

凡是通过 new Function()创建的对象都是函数对象(包括直接用function定义的函数对象和字面量定义的函数对象),其他的都是普通对象(Object、Function是JS自带的函数对象)。

  • 函数对象:
    function func1() {}
    var func2 = function() {};
    var func3 = new Function();

  • 普通对象
    var obj = {};
    var obj2 = new Object();
    var obj3 = new func1();

二、prototype与__proto__

在javascript中,每当定义一个对象(函数)的时候,对象中都会包含一些预定义的属性。其中每个函数对象都有一个prototype属性,这个属性指向函数的原型对象。(普通对象没有prototype属相,只有 __proto__ 属性)
原型对象就是普通对象(Function.prototype除外,因为它没有prototype属性。),原型对象就是该函数对象的一个实例。

function Animal() {}
var cat = new Animal();

此处的函数对象就是Animal,原型对象就是Animal.prtotype。

javascript在创建对象的时候,都有一个叫做__proto__的内置属性,用于指向创建它的函数对象的原型对象prototype

cat.__proto__ == Animal.prototype;
Aniaml.prototype.__proto__ == Object.prototype;

而Obect.prototype对象的__proto__属性等于null
而所有函数对象的__proto__属性都指向Function.prototype,它是一个空函数

Number.__proto__ === Function.prototype;
String.__proto__ === Function.prototype;
.
.
.

三、constructor

在默认情况下,所有的原型对象都会自动获得一个constructor属性,它是一个指向prototype属性所在的函数的指针

Animal.prototype.constructor == Animal;
cat.constructor == Aniaml;

constructor的值是一个函数,在javascript中,除了null 和undefined外的类型的 值、数组、函数以及对象 都有一个constructor属性,constructor的值就是这个 值、数组、函数或者对象的 构造函数

var num = 17;
var str = 'string1';
num.constructor    //Number()
str.constructor    //String()
.
.
.

四、引用类型

在javascript的7中数据类型中(包括ES6的symbol),只有对象时引用类型的值,其余为基本类型的值。
引用类型的值(对象)是引用类型的一个实例。对象时某个特定引用类型(也就是某个特别对象)的一个实例。新对象是使用new操作符 后跟一个构造函数来创建的。而构造函数本身就是一个函数(即一个函数对象)。
var obj = new Object();
引用类型的值不仅可以是Object,也可以是Array、Date、Function等
var arr = new Array();
因为typeof Array 的值为 “function”

五、原型继承

1.引用原型继承存在的问题

function Parents() {}
Parents.prototype.name = "father";
Parents.prototype.getName = function() {
    return this.name;
}

//子类继承
function Generator() {}
Generator.prototype = Parents.prototype;
Generator.prototype.name = "son";    //修改原型属性
Generator.prototype.constructor = Generator;

此时,Generator通过引用来继承Parents的属性,它们的引用指向了同一个内存空间,所以一旦修改了Generator的属性,Parents的属性也会受到影响。
为了解决父对象不受子对象修改的影响,使用临时构造器的方法。
首先创建一个临时构造器,当做一个中间变量,接着创建一个空函数,并将它的原型设置为父级的一个构造器,然后new一个中间变量(函数)来创建一个不包含父级属相的对象,同时又能从父级的prototype中继承父级的属性。

function Parents() {};
Parents.prototype.name = "father";
Parents.prototype.getName = function() {
    return this.name;
}

//创建临时构造器
var MiddleWare = Function();
MiddleWare.prototype = Parents.prototype;

//子对象
function Son() {};
Son.prototype = new MiddleWare();
Son.prototype.constructor = Son;    //将构造器指向自己
Son.prototype.name = "son";    //修改属性

var son = new Son();
console.log(son.getName());    //son
var parent = new Parents();
console.log(parent.getNmae());    //father 

2.浅拷贝
子对象拷贝父对象,父对象中的成员有对象变量时,子对象拷贝父对象的引用,若修改子对象,父对象的值也会受到影响。

var person = {
    name: "name",
    score: {math: 107,chinese: 118,english: 123}
};

var student = {age: 21};

function extend(first,second) {
    var second= second|| {};
    for(var prop in first) {
        second[prop] = first[prop];
    }
}

extend(person,student);    //拷贝
//修改子对象属性
studnet.score.math = 117;

console.log(person.score[math]);    //117;

此时,父对象中score.math的值也被修改了。

3.深拷贝
为了解决父对象总的引用属相不被修改,使用深拷贝来实现。
若父对象中的属性是对象或者是数组,对其做特殊处理:递归

function extendDeeply(first,second) {
   var second = second || {};
   for(var prop in first) {
       if(typeof first[prop] === "Object") {
           second[prop] = (first[prop].constructor === Array) ? [] : {};
           extendDeeply(first[prop],second[prop]);
       } else {
           second[prop] = first[prop];
       }
   }
}

也可以使用call来实现

function Parent() {
    this.name = "father";
    this.job = {one: "washing",tow: "running"}
}

function Son() {
    Parent.call(this);
    this.age = 21;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值