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属性,于是要到它指向的原型对象上面找,而它的原型对象依然是最初构造函数默认指向的那个原型对象,并不是后面赋值的字面两对象,所以是找不到的,因此报错。

原文地址:https://www.cnblogs.com/youyang-2018/p/11768306.html

时间: 2024-10-13 21:34:52

JS面向对象篇二、什么是原型?原型对象与实例对象、构造函数的关系及相关方法的相关文章

构造函数,原型对象,实例对象

一.构造函数,原型对象,实例对象 1.1 基本概念 1.对象: 属性和方法的集合,即变量和函数的封装. 调用构造函数产生的实例对象, 每个对象都有一个__proto__属性,指向这个对象的构造函数的原型对象. 2.构造器函数: 用于创建对象的函数,通过new关键字生成对象. 函数名一般首字母大写的. 每创建一个函数, 该函数都会自动带有一个prototype属性.该属性是一个指针,指向一个对象,该对象称之为原型对象(后期我们可以使用这个原型对象帮助我们在js中实现继承) 3.原型对象: 默认有一

JS面向对象-原型对象,实例对象,构造函数的关系

JS中每创建一个函数,该函数就会自动拥有一个prototype属性,为什么那??  因为最根上的object拥有一个prototype属性,而js中所有的对象又都继承自object,所以js中所有的对象都拥有一个prototype属性,而在js中函数也是对象,所以js中每个函数也都有一个prototype属性. 例如:function Person(){...} 和function Dog(){...} 而每一个prototype属性又会获得一个constructor属性 该constructo

JS面向对象笔记二

菜单导航,<JS面向对象笔记一>,  参考书籍:阮一峰之<JavaScript标准参考教程> 一.构造函数和new命令 二.this关键字 三.构造函数和new命令 四.构造函数和new命令 五.构造函数和new命令 六.构造函数和new命令 七.构造函数和new命令 八.构造函数和new命令 一.构造函数和new命令 1.构造函数 JavaScript语言的对象体系,不是基于"类"的,而是基于构造函数(constructor)和原型链(prototype) 为

JS面向对象(二)---继承

一.面向对象的继承 1.解析:在原有对象的基础上,略作修改,得到一个新的对象,并且不影响原有对象的功能 2.如何添加继承---拷贝继承 属性:call 方法: for in /* 继承:子类不影响父类,子类可以继承父类的一些功能(代码复用) 属性的继承:调用父类的构造1函数 call 方法的继承:for in 拷贝继承 */ //父类 function CreatePerson(name,sex){ this.name = name; this.sex = sex; } CreatePerson

js面向对象编程(二) 构造函数继承

构造函数绑定 //基类建筑物var building = function () {    this.spec = "building";}; //address:房子地址,toward:房子朝向var house = function (address, toward) {    this.address = address;    this.toward = toward;}; //使房子继承建筑物 //使用call或者apply方法,将父对象的构造函数绑定在子对象上,在子对象构造

JS面向对象篇三、创建对象的几种方法

先来说下最简单的两种创建对象的方式:通过Object构造函数创建以及对象字面量方式. Object函数创建对象 var person = new Object() person.name = 'youyang' person.age = 18 person.sayName = function () { console.log(this.name) } 对象字面量 var person = { name : 'youyang', age : 18, sayName : function () {

js面向对象(二)——继承

上一篇随笔讲了封装,这一篇我们说说继承,还是那上一篇猫和狗说事儿 function Dog(name,s){ this.name=name; this.sex=s; } Dog.prototype.type="犬科"; Dog.prototype.spack==function(){ alert("汪汪..."); } function Cat(name,s){ this.name=name; this.sex=s; } Cat.prototype.type=&qu

javascript原型对象与实例对象属性

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getS

构造函数、原型对象及其实例对象

构造函数有个 prototype 属性指向原型对象 实例化的对象有一个 [[prototype]] 属性指向原型对象 原型对象有一个 constructor 属性指向构造函数. 如果在实例的对象覆盖了原型对象中的某个属性或者方法后,会切断这个属性或方法指向原型的连接.即便后面把这个属性或者方法设置为 null,也不会恢复这个连接.只有通过 delete 操作符可以完全删除.注意:设置属性为 null 后调用这个属性,显示的是 null,设置方法为 null 后调用这个方法,显示的是 object