一文搞懂 JavaScript 的对象基础、原型与继承链(MDN 学习笔记)

面向对象的基础

1.声明一个对象

var myperson={
    name : "Zhang",
    age : 25,
    sex : "male",
    introduction:function(){
        alert(name+age+sex);
    }
};
myperson.introduction();

2.使用构造函数构造

构造函数与普通函数的区别在于调用方式,构造函数使用new方法构造。

function creatrPerson(name,age,sex,interests){
    this.name = name;
    this.age = age;
    this.sex = sex;
    this.interests = interests;
    this.bio = function(){
        var res = "";
        if(this.sex == "male"){
            res+="He ";
        }else if(this.sex=="female"){
            res+="She ";
        }else{
            res+="It "
        }
        for(var i=0;i<interests.length;i++){
            res+="likes "+interests[i];
            if(i!=interests.length-1){
                res+=" and ";
            }
        }
        alert(res);
    }
}
var zhang = new creatrPerson("zhang",18,"male",["basekatball","swiming","singing"]);
zhang.bio();

在这里插入图片描述
3.使用Object 构造

首先创建了一个空对象,之后使用点或括号表示法向此对象添加属性和方法

var person1 = new Object();
person1.name = 'Chris';
person1['age'] = 38;
person1.greeting = function() {
	alert('Hi! I\'m ' + this.name + '.');
}

对象原型

原型基础

JavaScript 常被描述为一种基于原型的语言 (prototype-based language)——每个对象拥有一个原型对象。

注意:与Java的面向对象设计不同,java中存在类的概念,创建对象后,会将类的属性和方法复制到对象中。
而JS中,会通过_proto_属性来建立构造方法与对象实例的连接。

原型链

每个 function方法中都具有 prototype 属性,其对应的也就是我们通过 function 构造出实例的 proto 属性。接下来举个例子。

function doSomething(){}
doSomething.prototype.foo = "bar"; // add a property onto the prototype
var doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value"; // add a property onto the object
console.log( doSomeInstancing );

对应输出如下,可以看到实例对象的_proto_对应构造函数的 protoype(因为设置了foo这个属性)。同时注意一点,JS会在需要某个对象的属性时,会首先去寻找该对象是否存在该属性,之后会去对象的_proto_属性中寻找,依次类推,如果找不到则会判断该属性为Undefined,这条链路被称为原型链

doSomeInstacing对象 的 proto 属性对应的的就是 dosomething构造函数 的 prototype 属性

{
    prop: "some value",
    __proto__: {
        foo: "bar",
        constructor: ƒ doSomething(),
        __proto__: {
            constructor: ƒ Object(),
            hasOwnProperty: ƒ hasOwnProperty(),
            isPrototypeOf: ƒ isPrototypeOf(),
            propertyIsEnumerable: ƒ propertyIsEnumerable(),
            toLocaleString: ƒ toLocaleString(),
            toString: ƒ toString(),
            valueOf: ƒ valueOf()
        }
    }
}

我们可以进一步理解原型链。我们使用之前cteatePerson方法去构造一个对象 Zhang。可以看到 Zhang 对象有多个属性或方法供我们使用。
在这里插入图片描述

  • 浏览器首先检查,Zhang 对象是否具有 valueOf() 方法。
  • 如果没有,则浏览器检查 Zhang 对象的原型对象(_proto_属性)是否 valueof() 方法。
  • 如果没有,则会进而调用 createPerson 构造函数的 prototype 对象,去 Object 构造函数中寻找是否具有 valueOf() 方法。这里有这个方法,于是该方法被调用。

原型链的访问顺序:Zhang->createPerson->Object

修改原型

1.prototype 属性

继承的属性和方法都在prototype中定义。这也就是为什么上文访问 Zhang.valueOf 能返回具体的值,因为Object.prototype.valueOf 可以供任何继承自它的对象使用。

而任何不在 prototype 中的属性或方法仅能供 Object 自己使用,继承自它的对象无法使用。

2.constructor 属性

每个实例对象都从原型对象那里继承了一个构造器,下述代码都会返回 CreatePerson()构造器。

person1.constructor
person2.constructor

我们同样可以使用构造器来构造对象。

person3 = new person1.constructor("feng",20,"male",["hha","aaa"]);

3.修改原型

我们尝试修改(添加)构造器中的prototype属性。

creatrPerson.prototype.farewell = function() {
  alert(this.name + ' has left the building. Bye for now!');
}

之后调用对象实例的 farewell 方法。浏览器会通过原型链找到构造器的prototype对象,并调用farewell方法

zhang.farewell()

在这里插入图片描述
值得注意的是,我们首先创建的的对象实例,之后才为构造器添加属性(方法),对象实例仍然能通过原型链找到该方法。

证明了下游对象(Zhang)在创建时不是复制了上游对象(构造函数的prototype)的方法和属性,而是建立了联系。在访问下游对象未定义的方法时,下游对象可以通过原型链访问到上游对象中的对应方法。

继承

可以通过上述文章了解,JS的继承和Java不同,是通过原型链来完成的。因此我们接着尝试创建继承自另一个对象的的JS对象。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章