面向对象-原型对象

230 阅读5分钟

这是我参与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(); // 猫跑了

图示:

9.png

不过这也存在一个问题:如果大批量的添加, 冗余代码比较多, 看起来比较乱。

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

七、总结

讲也讲了这么多了,总结一下下呗。但是我们这里不用文字总结,直接上图。这张图其实就算得上是原型链的操作了,不单单在这篇文章中有用,当我们记不太清的时候,看图不为也是总结思路的一种方式。

面向对象1.png

八、结语

码字不易,如果觉得对你有帮助,觉得还不错的话,欢迎点赞收藏~

当然由于是个人整理,难免会出现纰漏,欢迎留言反馈。