理清原型对象、 实例对象、构造函数

7,325 阅读5分钟

1.对象是什么

面向对象编程(OOP):具有灵活性,代码可复用性,高度模块化等特点。 1.对象是单个实物的抽象。 2.对象是一个容器,封装了对应属性和方法。 属性是对象的状态,方法是对象的行为。

2.构造函数

需要一个模版(表示一类实物的共同特征),让对象生成。 类(class)就是对象的模版。 js不是基于类的,而是基于构造函数(constructor)和原型链(prototype)。

构造函数特点: 1.函数体内使用this关键字,代表了所要生成的对象实例。 2.生成对象,必须使用new 关键字实例化。

Dog是构造函数,为了与普通函数区别构造函数的名字的第一个字母通常大写

  function Dog(name,age) {
// name和age就是当前实例化对象的属性
this.name = name
this.age = age
}
var dog1 = new Dog("小黑",5)
console.log('Dog 构造函数',Dog);
console.log('dog1 实例对象',dog1);

控制台打印:

3.instanceof 用法

instanceof运算符测试构造函数的prototype属性是否出现在对象的原型链中的任何位置

如果忘记写new关键字了,我们可以用instanceof 运算符来解决。

 function Person(name) {
// 判断this是否指向了当前的实例
if (this instanceof Person) {
// this指向了当前的实例,外部使用了关键字new
this.name = name
}else {
// this指向window,外部没有使用关键字new
return new Person(name)
}
}
var p1 = new Person("咚咚")
var p2 = new Person("锵锵")
console.log("p1",p1);
console.log("p2",p2);

控制台打印:

4.new关键字原理

new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。

new 关键字会进行如下的操作:

1.创建一个空对象,作为将要返回的对象实例 2.将这个空的对象原型对象,指向了构造函数的prototype属性对象 3.将这个实例对象的值赋值给函数内部的this关键字 4.执行构造函数内的代码。 5.如果该函数没有返回对象,则返回this。

  function Person(name) {
this.name = name
}
var p1 = new Person('咚咚')
console.log("p1:",p1);
console.log("p1.__proto__ === Person.prototype:",p1.__proto__ === Person.prototype);
console.log("Person.prototype:",Person.prototype);

控制台打印:

图解:

5.constructor属性

每个对象在创建时都会自动拥有一个构造函数constructor属性

function Person(name) {
this.name = name
}
var p1 = new Person("咚咚")
console.log("p1:",p1);
// constructor继承自原型对象,指向了构造函数的引用
console.log("Person.prototype:",Person.prototype);
console.log("p1.constructor === Person:",p1.constructor === Person);

控制台打印:

6.使用构造函数创建对象的利与弊

:实例对象具有相同的属性和方法,方便调用。 :实例对象调用了相同的方法,占用了内存空间,浪费系统资源。

  function Person(name) {
this.name = name
this.sayName = function () {
console.log(this.name);
}
}
var p1 = new Person("Tom")
var p2 = new Person("Jack")
console.log("p1:",p1);
console.log("p2:",p2);

控制台打印: image-20200315145202164

7.原型对象

原型对象:Foo.prototype。 实例对象:f1 就是实例对象,每个实例对象( f1 )都有一个私有属性(称之为 proto )指向它的构造函数的原型对象(prototype ),每一个实例对象都有一个constructor属性,这个constructor是通过继承关系继承来的,它指向了当前的构造函数Foo。 构造函数:用来初始化新创建对象的函数,Foo是构造函数,自动给构造函数赋予一个prototype属性,该属性指向了实例对象的原型对象。

  function Foo() {}
Foo.prototype.showName = function () {
console.log('咚咚');
}
var f1 = new Foo()
var f2 = new Foo()
console.log("f1.showName():",f1);
console.log("f2.showName():",f2);
f1.showName()
f2.showName()

控制台打印: ![image-20200315151518716](https://tva1.sinaimg.cn/large/00831rSTgy1gcuqzypnqzj30bh062jrp.jpg)

8.搞清它们三者之间的关系

  function Foo() {}
var f1 = new Foo()
console.log("f1",f1);
console.dir(Foo);
console.log("Foo.prototype === f1.__proto__:",Foo.prototype === f1.__proto__);
console.log("f1.__proto__.__proto__:",f1.__proto__.__proto__);

控制台打印:

图解:

image-20200315155028181
image-20200315155028181

9.prototype属性的作用

js继承机制:通过原型对象实现继承。 原型对象的作用就是定义所有实例对象共享的属性和方法。

  function Foo(){}
Foo.prototype.name = "咚咚"
var f1 = new Foo()
var f2 = new Foo()
console.log(f1.name);
console.log(f2.name);
Foo.prototype.name = "锵锵"
console.log(f1.name);
console.log(f2.name);

控制打印: image-20200315160020204

10.原型链

js规定:所有的对象都有自己的原型对象。

原型链:对象的原型=>原型的原型=>原型的原型的原型=====>null

1.根据原型链查找,如果一层一层往上查找,所有的对象的原型最终都可以寻找得到Object.prototype,Object构造函数的prototype 2.所有的对象都继承了Object.prototype上的属性和方法 3.读取属性和方法的规则:js引擎会先寻找对象本身的属性和方法,如果找不到,就到它的原型对象去找,如果还是找不到,就到原型的原型去找,如果直到最顶层的Object.prototype还是找不到, 如果对象和它的原型,都定制了同名的属性,那么优先读取对象自身属性,这也叫覆盖

  function Person(name) {
this.name = name
this.showName= function () {
console.log('咚咚');
}
}
Person.prototype.showName = function(){
console.log(this.name);
}
var p1 = new Person("小红")
var p2 = new Person("小明")
p1.showName()
p2.showName()

控制台打印:

image-20200315161712900
image-20200315161712900

修改原型对象的注意点

  • 一旦我们修改构造函数的原型对象,为了防止引用出现问题,同时也要修改原型对象的constructor属性。
  function MyArray(){}
MyArray.prototype = Array.prototype;
console.dir(MyArray.prototype.constructor);
MyArray.prototype.constructor = MyArray
var arr = new MyArray()
console.log("arr:",arr);
arr.push(1,2,3)
console.log("arr:",arr);
console.dir(arr.constructor);
console.log("arr.__proto__ === MyArray.prototyep:",arr.__proto__ === MyArray.prototype);

控制台打印:

image-20200315163120949
image-20200315163120949

11.总结

    // 构造函数:Foo
// 实例对象:f1
// 原型对象:Foo.prototype
function Foo() {}
var f1 = new Foo()
// 1.原型对象和实例对象的关系
console.log(Foo.prototype === f1.__proto__); // true
// 2.原型对象和构造函数的关系
console.log(Foo.prototype.constructor === Foo); // true
// 3.实例对象和构造函数
// 间接关系是实例对象可以继承原型对象的constructor属性
console.log(f1.constructor === Foo); // true
// 注意:代码顺序很重要
Foo.prototype = {}
console.log(Foo.prototype === f1.__proto__); // false
console.log(Foo.prototype.constructor === Foo); // false

参考资料

[1]更多文档: https://developer.mozilla.org/zh-CN/