zoukankan      html  css  js  c++  java
  • JS面向对象篇二、什么是原型?原型对象与实例对象、构造函数的关系及相关方法

    本文内容:
    1、构造函数、原型对象与实例对象之间的关系;
    2、isPrototypeOf()和Object.getPrototypeOf();
    3、实例对象上与原型对象上同名的属性处理;
    4、hasOwnProperty()方法和in操作符判断属性来自实例对象本身还是它的原型对象;
    5、for-in、Object.keys()和Object.getOwnPropertyNames()方法获取实例对象或者原型对象上的属性;
    6、需注意的特殊问题

    构造函数、原型对象与实例对象

    function Person () {}
    Person.prototype.name = 'zhangsan'
    Person.prototype.age = 23
    Person.prototype.sayName = function () {
    	console.log(thi.name)
    }
    var p1 = new Person()
    p1.sayName() // 'zhangsan'
    var p2 = new Person()
    p2.sayName() // 'zhangsan'
    

    首先每一个函数都有一个prototype属性,这个属性指向一个对象,这个对象就是原型对象
    默认情况下,所有原型对象都会自动获得一个constructor属性,这个属性指向prototype属性所在的函数,也就是构造函数
    当调用构造函数创建一个实例对象后,该实例对象有一个内部属性:[[prototype]](在部分浏览器中可通过__proto__访问到),这个属性指向构造函数的原型对象
    如在上面的例子中:构造函数Person有一个prototype属性,它指向原型对象,而原型对象默认有且只有一个constructor属性,该属性指向构造函数Person,即Person.prototype.constructor指向Person,实例对象p1和p2也都包含一个内部属性([[prototype]]),该属性指向Person.prototype。
    它们之间的关系如图:
    在这里插入图片描述
    另外还需要注意的一个问题:
    虽然p1和p2两个实例中并没有sayName(),但是在例子中我们也能访问到,成功打印出‘zhangsan’,这是通过查找对象属性来实现的,其过程如下:
    每当代码读取到某个属性时,都会执行一次搜索,首先从实例对象本身开始,如果实例对象上找到了该属性,则返回该属性值,若没有找到,则继续到实例的[[prototype]]指向的原型对象上去找,也就是去创建此实例的构造函数的prototype指向的原型对象上找,找到了就返回该属性值。
    调用p1.sayName(),首先p1上没有定义该属性,那么要到Person.prototype中去找,Person.prototype中有sayName()的定义,则取该函数。

    isPrototypeOf()和Object.getPrototypeOf()

    因为[[prototype]]为内部属性,我们无法直接访问到它,但是我们可以通过isPrptotypeOf()方法来确定对象与它的构造函数的原型对象之间的关联;

    console.log(Person.prototype.isPrototypeOf(p1)) // true
    console.log(Person.prototype.isPrototypeOf(p2)) // true
    

    ECMAScript5中新增了Object.getPrototypeOf()方法用来获取一个对象的原型:

    console.log(Object.getPrototypeOf(p1) == Person.prototype) // true
    console.log(Object.getPrototypeOf(p1).name) // 'zhangsan'
    

    实例对象上定义的与原型对象同名的属性

    在实例对象上定义了与原型对象上同名的属性后,会屏蔽掉原型对象的该属性,访问的是实例对象上的属性。

    hasOwnProperty()

    使用hasOwnProperty()方法能够判断某个属性是否是实例对象本身的属性。

    function Person () {}
    Person.prototype.name = 'zhangsan'
    var p1 = new Person()
    console.log(p1.hasOwnProperty('name')) // false
    p1.job = 'coder'
    console.log(p1.hasOwnProperty('job')) // true
    

    in操作符

    in操作符,可以判断属性是否可以通过对象访问到,无论是原型上的属性还是实例上的。

    console.log('name' in p1) // true
    console.log('job' in p1) // true
    

    结合in操作符和hasOwnProperty()方法就能够准确的判断出,一个属性是实例上对象本身的,还是原型对象上的。

    function isPrototypeProperty (obj, name) {
    	return !obj.hasOwnProperty(name) && name in obj
    }
    

    遍历对象属性

    for-in

    for-in循环返回的事所有能够通过对象访问的、可枚举的(enumerable)属性,既包含存在于实例中的属性,又包含原型中的属性。

    Object.keys()

    ECMAScript5中新增的方法,返回的是所有可枚举的实例属性的字符串数组

    function Person () {}
    Person.prototype.name = 'zhangsan'
    Person.prototype.sayName = function () {
    	console.log(this.name)
    }
    console.log(Object.keys(Person.prototype)) // ['name', 'sayName']
    var p1 = new Person()
    p1.name = 'lisi'
    p1.age = 23
    console.log(Object.keys(p1)) // ["name", "age"]
    

    Object.getOwnPropertyNames()

    返回所有的实例属性,无论它是否可枚举。

    function Person () {}
    Person.prototype.name = 'zhangsan'
    Person.prototype.sayName = function () {
    	console.log(this.name)
    }
    console.log(Object.keys(Person.prototype)) // ['constructor','name', 'sayName']
    

    更简单的原型语法

    之前的代码中,每添加一个属性就要写一遍Person.prototype,为了简化代码,可以做优化如下:

    function Person() {}
    Person.prototype = {
    	name : 'zhangsan',
    	age : 23,
    	sayName : function () {
    		console.log(this.name)
    	}
    }
    var p1 = new Person()
    

    要注意这样做会对对原型对象的constructor有影响,之前提到,每一个函数都有一个prototype属性,这个属性自动获取了一个constructor属性,它指向构造函数,但是上面这种写法相当于给Person的prototype属性重新赋予了一个新的对象,而这个对象不存在constructor属性了。

    另外一个问题:重写构造函数的原型和调用构造函数创建实例的顺序很重要!
    当我们像下面这样做时会导致报错:

    function Person () {}
    var p1 = new Person()
    Person.prototype = {
    	name : 'zhangsan',
    	sayName : function () {
    		console.log(this.name)
    	}
    }
    console.log(p1.name) // 报错
    

    如上代码,由于先创建了实例对象,这时p1的[[prototype]]属性指向了Person.prototype,即构造函数Person的原型对象,接着,重写了Person.prototype,再访问p1.name的时候,由于p1实例上找不到name属性,于是要到它指向的原型对象上面找,而它的原型对象依然是最初构造函数默认指向的那个原型对象,并不是后面赋值的字面两对象,所以是找不到的,因此报错。

  • 相关阅读:
    vs2015调试慢
    阿里正式发布《Java开发手册》终极版!
    为什么听有些人讲话让人抓狂
    hibernate 映射实例 学生 课程 成绩
    hibernate 树状映射
    hibernate 一对多双向的CURD
    hibernate 多对多双向关联
    hibernate 多对多单向
    hibernate 一对多双向
    hibernate 一对多关联
  • 原文地址:https://www.cnblogs.com/youyang-2018/p/11768306.html
Copyright © 2011-2022 走看看