这是我参与8月更文挑战的第15天,活动详情查看:8月更文挑战
前言
在javascript中,每当定义一个对象时,对象中都会包含一些预定义的属性。其中每个函数对象都有一个prototype属性,这个属性指向函数的原型对象。
上篇我们讲了构造函数的使用,今天来搞一下构造函数上的原型对象,原型对象方法的读取和设置、访问函数原型对象的方式、hasOwnProperty和in属性操作。
一、面向对象-对原型对象扩展属性和方法
这里主要的是就两种方式,直接替换或者替换原型对象
1. 借助对象的动态性, 添加属性和方法
function Cat(name, age) {
this.name = name;
this.age = age;
}
// 添加一个行为
c1.prototype.run = function () {
console.log('猫跑了');
}
var c1 = new Cat('花花', 1);
c1.run(); // 猫跑了
图示:
不过这也存在一个问题:如果大批量的添加, 冗余代码比较多, 看起来比较乱。
2. 替换原型对象
原型对象本身就是一个对象,它与函数之间, 是通过函数的prototype属性进行关联; 所以可以直接创建一个对象,当做是原型对象, 然后修改函数的prototype指针指向。
function Cat() {
// this.name = '花花',
// this.age = 1
}
Cat.prototype.sayName = function () {
console.log('花花');
}
var c1 = new Cat();
// 重写原型
Cat.prototype = {
constructor: Cat,
name: "球球",
age: 2,
sayName: function () {
console.log('name ==> ', this.name);
}
};
var c2 = new Cat();
c1.sayName(); // 花花
c2.sayName(); // 球球
说明: 首先实例化的时候,
c1的原型指针指向了构造函数的原型对象,这个的原型对象只有一个sayName的方法,输出的是花花。然后给构造函数创建了新的原型对象,也就是构造函数指向了新的原型对象,但是实例c1的原型指针依然还是原来的空原型对象{}
注意替换原型对象,和创建对象的先后顺序 ----
替换在前,创建在后
替换总结
- ① 当替换构造函数的原型对象的时候,已经使用构造函数创建出来的对象指向的原型对象不会发生改变
- ② 如果是替换了构造函数的原型对象,那么构造函数的新的原型对象和旧的原型对象之间没有任何关系
二、面向对象-原型对象属性-方法的读取和设置
1. 设置原型对象属性/方法
(1) 对象.属性 = xxx
如果该属性在对象中已经存在,则修改该属性的值
如果该属性在对象中尚未存在,则新增该属性
错
(2) 原型对象.属性 = xxx
一定要获取到原型对象来设置
函数名.prototype
(3) 特例
如果原型对象的属性, 是一个引用类型的
那么通过对象也可以修改(操作对象内部属性)
影响所有对象
2. 访问原型对象属性/方法
构造函数创建出来的对象在访问属性的时候,会先在实例内查找,如果没有找到则进一步到对应的原型对象中查找。
function Cat(name, age) {
this.name = name,
this.age = age
}
Cat.prototype.sayName = function () {
console.log('花花');
}
var c1 = new Cat('花花', 1);
c1.sayName();
这里本身实例上是没有sayName这个方法的,所以就会往__proto__里寻找。如果还没有的话就会一直到,直到Object身上还是没有就返回null,这个就是原型链。
问:为什么不是用prototype呢? 答:首先,
prototype是每个函数才有的属性,而__proto__是每个对象都有的属性,你new出来的实例就是一个对象。大多数情况下,__proto__可以理解为“构造器的原型”,即__proto__===constructor.prototype。但有时候也不是相等的。
三、面向对象-访问函数原型对象的方式
function Cat(name, age) {
this.name = name,
this.age = age
}
Cat.prototype.color = '灰色'
Cat.prototype.sayName = function () {
console.log('花花');
}
var c1 = new Cat('花花', 1);
1. 通过函数名.prototype
console.log(Cat.prototype.color); // 灰色
2. 通过对象中的__proto__属性访问
console.log(c1.__proto__.color); // 灰色
注意点说明
- __proto__是一个非标准属性
- 即ECMAScript中并不包含该属性,这只是某些浏览器为了方便开发人员开发和调试而提供的一个属性,
不具备通用性 - 建议: 在调试的时候可以使用该属性,但不能出现在正式的代码中
四、面向对象-hasOwnProperty和in属性操作
1. in
判断一个对象, 是否拥有某个属性(如果对象身上没有, 会到原型对象里面查找),返回一个布尔值。
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.address = '广州';
var p = new Person('Jack', 18);
// console.log(name in p); // false
console.log('name' in p); // true
console.log('address' in p); // true
2. hasOwnProperty
只到对象自身查找,同样返回一个布尔值。
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.address = '广州';
var p = new Person('Tom', 20);
console.log(p.hasOwnProperty('name')); // true
console.log(p.hasOwnProperty('address')); // false
五、面向对象-isPrototypeOf和instanceOf
1. isPrototypeOf
判断一个对象, 是否是某个实例的原型对象
function Person() {}
// Person.prototype
var p = new Person();
console.log(Person.prototype.isPrototypeOf(p)); // true
console.log(p.__proto__.isPrototypeOf(p)); // true
// 对象
/*
var obj = {
'name': 'Jack'
};
Person.prototype = obj;
console.log(Person.prototype.isPrototypeOf(p)); // false
*/
var obj = {
'name': 'Jack'
};
Person.prototype = obj;
var p2 = new Person();
console.log(Person.prototype.isPrototypeOf(p2)); // true
2. instanceOf
判断一个对象, 是否是某个构造函数的原型链上
function Person() {}
var p = new Person();
console.log(p instanceof Person); // true
var obj = {
name: 'Jack'
};
Person.prototype = obj;
var p2 = new Person();
console.log(p2 instanceof Person); // true
console.log(p2); // Person {}
六、面向对象-原型完善-constructor
用于获取一个对象的真实类型
function Person() {}
var p = new Person();
console.log(p); // Person {}
// console.log(p.constructor.name);
Person.prototype = {
constructor: Person, // 一定要设置, 指向Object
name: '撩课',
age: 18
};
var p1 = new Person();
console.log(p1); // Person {}
console.log(p1.constructor.name); // Person
七、总结
讲也讲了这么多了,总结一下下呗。但是我们这里不用文字总结,直接上图。这张图其实就算得上是原型链的操作了,不单单在这篇文章中有用,当我们记不太清的时候,看图不为也是总结思路的一种方式。
八、结语
码字不易,如果觉得对你有帮助,觉得还不错的话,欢迎点赞收藏~
当然由于是个人整理,难免会出现纰漏,欢迎留言反馈。