Js面向对象及原型原型链总结

面向对象设想

面向对象都有的观点,所以都能够经由历程类建立雷同属性要领(统一类嘛)的若干个对象,然则ECMAScript中没有类,所以它的对象和基于类的语言中的对象有所不同。对象的基本表现情势:

var person = {
    name:'Nicholas',
    age:29,
    job:'Soft Engineer',
    sayName() {
        alert(this.name); //this.name被剖析为person.name
    }
}

属性范例

JavaScript中为了完成JavaScript引擎划定了内部属性,内部属性是不可直接接见的,用[[property]]示意。ECMA有两种属性:数据属性和接见器属性

数据属性

应用这些数据属性变动操纵对象以下:

          var person = {
              name:'liming', // [[Value]]特征将被设置为liming,值得转变反应在[[Value]]上
              age:'23', // 同上
          }
          Object.defineProperty(person,'age',{
             writable:false, // 不可改值
             configurable:false // 不可删除值,
          })
  
  

注重configurable只能修正一次,没后悔药可吃。另有Object.defineProperty()假如默许不制订第三个参数,那末将属性的特征默许悉数为false,这个要领在IE8上只管不要用。

接见器属性

接见器属性不包括数据值,他包括一对儿getter和setter(不过这两个都不是必需的).他的核心作用是当一个属性转变的时刻,另一个属性也跟着他转变。他俩必需同时涌现,一下代码能够很好的邃晓上面的话:

    var article = {
        _year:2018,
        edition: 1
    }
    Object.defineProperty(article,'year',{
        get: function() {
            return _this.year
        }
        set:function(val) {
            if(val > 2018) {
                this._year = val;
                edition += val - 2018;
            }
        }
    })

读取属性的特征

在这个例子中

var book = {};
Object.defineProperties(book,{
    _year: {
        writable:true,
        value:2018
    },
    edition: {
        writable:true,
        value:1
    },
    year: {
        get:function() {
            return this._year
        },
        set:function(val) {
            if(val > 2018) {
                this._year = val;
                this.edition += val - 2018;
            }
        }
    }
})

我们能够应用Object.getOwnPropertyDescriptor的要领取得属性的形貌符。它返回一个对象,因而在上面的代码中,到场以下代码:

var descriptor = Object.getOwnPropertyDescriptor(book,'_year');
// {value: 2018, writable: true, enumerable: false, configurable: false}

var descriptor2 = Object.getOwnPropertyDescriptor(book,'year');
// {get: ƒ, set: ƒ, enumerable: false, configurable: false}

建立对象

1.工场情势


function createPerson(name,age,job) {
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function() {
        alert(this.name)
    }
    return o;
}
var person1 = createPerson('namea',27,'joba') // {name: "namea", age: 27, job: "joba", sayName: ƒ}
var person2 = createPerson('nameb',17,'jobb') // {name: "nameb", age: 17, job: "jobb", sayName: ƒ}

2.组织函数模子

    function Person(name,age,job) {
        this.name = name;
        this.age = age;
        this.job = job;
        this.sayName = function() {
            alert(this.name);
        };
    }
    person1 = new Person('namea',27,'joba');
    person2 = new Person('nameb',17,'jobb');

在建立Person实例的历程当中,必须要new出来,以这类体式格局挪用组织函数会阅历以下四个历程:
1)建立一个新对象
2)把组织函数中的this指向这个新对象
3)实行组织函数的代码
4)返回新对象

实例化组织函数的新对象后,他的对象范例是组织函数范例,同时也是Object范例

function Person(name,age,job) {
        this.name = name;
        this.age = age;
        this.job = job;
        this.sayName = function() {
            alert(this.name);
        };
    }
    person1 = new Person('namea',27,'joba');
    console.log(person1 instanceof Person) // true
    console.log(person1 instanceof Object) // true
    
    

这条划定规矩,也是它赛过工场情势的一个缘由

原型情势

function Person (){}
Person.prototype.name ='andy';
Person.prototype.age = 25;
Person.prototype.sayName = function() {
    alert(this.name)
}
var person1 = new Person();
var person2 = new Person();

person1.sayName === person2.sayName是对的,因为他们配合指向一个援用,而组织函数范例就不一样,所以能够说在肯定方面节约了内存。想知道组织函数实例化的对象和组织函数之间原型的关联,要看书上图6-1,当能默写出来基本就邃晓了。文字形貌以下:
当建立一个新函数时,都邑建立一个prototype属性->这个属性指向函数的原型对象(邃晓为prototype指向的是一个对象) -> 一切原型对象又会自动取得一个constructor属性,而它又指向适才建立的这个函数(绕回来了)。所以上面的历程能够邃晓为Person === Person.prototype.constructor // true
第二点,我们经由历程组织函数建立的实例有一个私有属性__proto__,这个私有属性指向组织函数的原型对象,即person1.__proto__ === Person.prototype // true。同理我们能够再继续往上推,组织函数Person是个函数对象,那末Person也应该有__proto__,他的私有属性应该是Function.prototype 即Person.__proto__ === Function.prototype //true

实例中的属性和原型中的属性

建立实例属性和原型中的属性重名的时刻,他们会同时存在,然则你猎取对象的属性时,会根据先在实例中搜刮再从原型中搜刮的递次为搜刮准绳,纵然将实例中的同名属性设置为null,也会读取实例属性,而用delete这个属性后,实例中搜不到了,才会去原型中猎取。

function Person() {}
Person.prototype.name = 'andy';
Person.prototype.sayName = function() {
    console.log(this.name);
};
Person.prototype.age = 25;

var person1 = new Person();
var person2 = new Person();
person1.name = 'liming';
console.log(person1.name) // liming
console.log(person2.name) // andy
person1.name = null;
console.log(person1.name) // null
delete person1.name // andy

hasOwnProperty

用于检测属性是存在于原型中照样实例中。因为是Object继续下来的要领,所以固然是存在于对象中返回true,上面的例子加上以下处置惩罚:

function Person() {}
Person.prototype.name = 'andy';
Person.prototype.sayName = function() {
    console.log(this.name);
};
Person.prototype.age = 25;

var person1 = new Person();
var person2 = new Person();
person1.hasOwnProperty('name') // false
person1.name = 'liming';
person1.hasOwnProperty('name') // true

原型与in操纵符(in与for in)

  • in操纵符返回的是true和false,不管在实例中,照样在原型中in都能找到,而hasOwnProperty()只能检测出在实例中,而对象中有没有就不清晰了。所以引伸出了一个检测只存在于原型中的要领,以下:
    function hasPrototypeProperty(obj,property) {
        return !obj.hasOwnProperty(property) && (property in obj);
    }
    function Person() {};
    Person.prototype.msg = "I'm only in prototype";
    var person1 = new Person();
    person1.name = "andy";
    hasPrototypeProperty(person1,'msg'); // true
    hasPrototypeProperty(person1,'name'); // false
  • 想猎取对象中实例和原型中的一切可罗列属性:for-in([[Enumerable]]为true)的,constructor,hasOwnProperty(),toString()…这类默许都设置成了false,所以罗列不出来他们,比方:

       function Person() {};
       Person.prototype.msg = "I'm only in prototype";
       person1.__proto__.age = 25; // 和上面定义体式格局的效果一样
       var person1 = new Person();
       person1.name = "andy";
       
       
       for(var prop in person1) {
           console.log(prop) // name msg age  //没有constructor,因为不可罗列
       }
  • 想猎取对象中或实例和原型中的一切可罗列属性:Object.keys()
    //接上个例子
    Object.keys(Person.prototype); // ['msg','age']
    Object.keys(person1) // ["name"]
  • 想猎取对象中实例或原型中的一切可罗列和不可罗列属性:Object.getOwnPropertyNames()
    Object.hasOwnPropertyNames(Person.prototype) //  ["constructor", "msg", "age"]

字面量情势定义对象原型

上面的体式格局中,定义原型属性的时刻都太甚烦琐,能够用对象字面量的情势来定义,以下:

function Person() {};
Person.prototype = {
    age:25,
    name:'andy',
    job:'Soft engineer',
    sex:'man'
}

但如许定义发生了一个题目 -> constructor的指向不再是Person了,因为我们重写了Person.prototype(constrctor是原型对象中自动天生的属性),假如constructor真的很主要,须要我们如许写原型,再以上基本增添一个constructor属性:

function Person() {};
Person.prototype = {
    constructor:Person,
    age:25,
    name:'andy',
    job:'Soft engineer',
    sex:'man'
}

但如许写又有一个题目 -> constructor的[[Enumerable]] 从默许的false变成true了。所以还要应用前面的学问,Object.defineProperty(Person.prototype,'constructor'),就和之前一样了。

原型的动态性:

用字面量重写了对象的原型后,现实上会转变对象原型的指针,如高程书中6-3,形貌的异常抽象,这里看一个例子就可以深入邃晓了:

    var friend = new Person();
    Person.prototype.sayHi = function() {
        alert('hi');
    };
    friend.sayHi(); // hi

实例与原型存在松懈关联,所以援用这个要领不会有题目;

    function Person() {}
    var friend = new Person();
    Person.prototype = {
        ......,
        sayHi: function() {
            alert('hi');
        };
    }
    friend.sayHi(); // error

原生对象的原型

要邃晓,我们的援用范例,比方arr = new Array 的Array,Object,String…背地也都有prototype原型的,内里有不少要领,固然我们能够为它增添要领,比方:

String.prototype.startWith = function(word) {
    return this.indexOf(word) === 0;
};
'kdsfkjs'.startWith('abc') // false;
'kdsfkjs'.startWith('kdsf') // true;

这类写法虽然很好,然则不发起用,因为能够发生定名争执等题目。

原型对象的瑕玷

它最大的题目在于同享援用范例的属性上面,以下例子

    function Person() {}
    Person.prototype = {
        constructor: Person,
        name: 'andy',
        age: 25,
        friend : ['shelby','jony'],
        sayHi : function() {
            alert('Hi');
        }
    }
    var person1 = new Person();
    var person2 = new Person();
    
    person1.friend.push('lee') // 目标是给person1这个对象里的friend增添本身的一个值。
    
    alert(person1.friend) //['shelby','jony','lee']
    alert(person2.friend) //['shelby','jony','lee']

因为friend是援用范例属性,指向一个地点,而friend最初还在prototype中,所以给援用范例操纵后,只是在援用地点稳定的前提下修正属性值,而person2的friend属性也援用该地点,所以就会发生我们不想要的效果。

组合应用组织函数和原型

组织函数用来实例化奇特的属性,而原型要领能够建立公有的要领,节约内存,所以他俩组合应用是很好的体式格局。

function Person(name,age,job) {
    this.age = age;
    this.name = name;
    this.job = job;
    this.friends = ["lee","jony"];
}
Person.prototype = {
    constructor:Person,
    sayName:function() {
        alert(this.name)
    }
}
var person1 = new Person("Nicholas",29,"Software engineer");
var person2 = new Person("Greg",27,"Doctor");
person1.friends.push('marry');
console.log(person1.friends) // ["lee","jony","marry"];
console.log(person2.friends) // ["lee","jony"]

继续

每一个组织函数都有一个原型对象(prototype),而原型对象中都有一个默许指向组织函数的指针(constructor),组织函数实例化后,实例化的对象又有一个内部指针(__proto__)指向原型对象所以他们是存在自内而外的一个逐层挪用的关联。须要把继续链的图捯饬清晰就和上面原型链的学问没什么区别了

    原文作者:andy00614
    原文地址: https://segmentfault.com/a/1190000015904448
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞