开头

笔记视频内容源自B站Javascript从入门到放弃 第十二章 面向对象编程,笔记为自行整理复习使用,欢迎一同学习交流,转载请通知原作者

一、原型、原型链1.1、什么是面向对象?

面向对象编程是目前主流的的一个编程的模式,它将我们真实世界各种复杂的关系抽象成了一个个的对象,然后由这些对象的分工合作完成真实世界的模拟。每一个对象都有其功能中心,即完成的任务(方法,属性)。因此面向对象编程(OOP)具有灵活、代码可复用、高度模块化等特点。

1.对象是单个实物的抽象

2.对象是一个容器,封装了对应的属性方法,属性是对象的状态,方法是对象的行为(完成的任务)

1.2、构造函数实例化对象

生成一个对象通常需要一个模板(表示一类实物的共同特征),让对象生成

类(class)就是对象的模板,但是js不是基于类的,而是基于构造函数(constructor)和原型链(prototype),我们来看一段代码:

<body> <script> function Dog(name, age) { //name和age就是当前实例化对象的属性 this.name = name; this.age = age; } var dog1 = new Dog("阿黄", 10); </script> </body>

javascript中的原型链(JS面向对象编程笔记)(1)

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

构造函数的特点

  1. 函数体内使用this关键字,代表了所要生成的对象实例
  2. 生成对象必须通过new关键字实例化

如果没有new关键字,Dog函数为普通函数没有返回值,结果就是undefined

<body> <script> function Dog(name, age) { //name和age就是当前实例化对象的属性 this.name = name; this.age = age; } // var dog1 = new Dog("阿黄", 10); var d = Dog("阿黄", 10); console.log(d); </script> </body>

javascript中的原型链(JS面向对象编程笔记)(2)

使用严格模式报错会进一步扩大:

<body> <script> function Dog(name, age) { 'use strict' this.name = name; this.age = age; } var d = Dog("阿黄", 10); console.log(d); </script> </body>

javascript中的原型链(JS面向对象编程笔记)(3)

1.3、instanceof用法

instanceof表示当前对象是否是它的类的实例,如果是返回true,不是返回false

<body> <script> function Dog(name){ if(!(this instanceof Dog)){ //this指向了window,外部没有使用关键字new return new Dog(name) } //this指向当前实例,外部使用了关键字new this.name = name; } var d1 = Dog('阿黄') console.log(d1); </script> </body>

javascript中的原型链(JS面向对象编程笔记)(4)

上面的代码利用instanceof关键字来判断,如果是作为普通函数进来,this的指向为window而不是Dog,返回一个new过后的构造函数,如果是则赋值其name属性

1.4、new命令原理

当我们使用new命令时发生了什么呢?我们使用一段代码来研究一下:

<body> <script> function Person(name){ this.name = name } var p1 = new Person(); console.log(p1); console.log(p1.__proto__ === Person.prototype) //true console.log(Person.prototype); </script> </body>

首先new关键字构造函数后:

  1. 创建一个空对象,作为将要返回的对象实例
  2. 将空对象的原型对象指向了构造函数的prototype属性对象

3.将这个实例对象的值赋值给函数内部的this关键字

​ 4.指向构造函数体内的代码

javascript中的原型链(JS面向对象编程笔记)(5)

1.5、constructor属性

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

<body> <script> function Person(name){ this.name = name } var p1 = new Person('chen'); console.log(p1); console.log(p1.constructor); console.log(p1.constructor === Person); console.log(Person.prototype); </script> </body>

javascript中的原型链(JS面向对象编程笔记)(6)

constructor继承自原型对象,其中指向了构造函数的引用,所以p1.constructor === Person应该为true

使用构造函数创建对象的好处在于,能够共享内部属性和方法:

<body> <script> 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,p2); </script> </body>

就像p1p2,它们都各自用有自己的name属性。但同样的也有弊端:每个实例调用的时候方法都要重新创建,存在冗余,想要解决这类方法要使用原型对象(prototype)来解决

javascript中的原型链(JS面向对象编程笔记)(7)

1.6、原型对象介绍

先看代码:

<body> <script> function Foo() {} Foo.prototype.name = "chen"; Foo.prototype.showName = function () { console.log("fchen"); }; var f1 = new Foo(); var f2 = new Foo(); console.log(f1.name); console.log(f2.name); f1.showName(); f2.showName(); </script> </body>

原型对象:Foo.prototype

实例对象:f1就是实例对象,实例对象可以构造多个,每一个原型对象中都有一个__proto__,每个实例对象都有一个constructor属性,这个constructor通过继承关系继承来的,它指向了当前的构造函数Foo

构造函数:用来初始化新创建对象的函数,Foo就是构造函数,自动给构造函数赋予一个属性prototype,该属性指向了实例对象原型对象(__proto__)

javascript中的原型链(JS面向对象编程笔记)(8)

1.7、原型链

<body> <script> function Foo(){}; var f1 = new Foo(); </script> </body>

以上面的代码结合图示来分析一下Foo构造函数的原型链:

javascript中的原型链(JS面向对象编程笔记)(9)

Foo的实例对象f1,它的原型对象f1.__proto__指向了Foo的prototype属性,f1的constructor属性间接继承Foo,如果将Foo.prototype也当作一个实例化的对象,同样的他也会有__proto__与constructor属性,Foo.prototype.__proto__指向了Object.prototype,``Foo.prototype.constructor**直接继承**于Foo`,如图所示:

javascript中的原型链(JS面向对象编程笔记)(10)

javascript中的原型链(JS面向对象编程笔记)(11)

控制台与图示一致,f1.__proto__ === Foo.prototype

再深入下去,将Foo当成一个实例对象,它的__proto__又将指向哪一个呢:

javascript中的原型链(JS面向对象编程笔记)(12)

javascript中的原型链(JS面向对象编程笔记)(13)

Object实例对象的__proto__为null

javascript中的原型链(JS面向对象编程笔记)(14)

javascript中的原型链(JS面向对象编程笔记)(15)

FooObject再当成一个实例,它们也有对应的constructor

javascript中的原型链(JS面向对象编程笔记)(16)

javascript中的原型链(JS面向对象编程笔记)(17)

__proto__也同理:

javascript中的原型链(JS面向对象编程笔记)(18)

函数也是一个对象,任何函数我们可以看作是Functionnew出来的对象,如果我们把Foo当作对象它同样的有__proto__和constructor属性

再再深究下去Funtion实例对象也有其__proto__和constructor属性

javascript中的原型链(JS面向对象编程笔记)(19)

javascript中的原型链(JS面向对象编程笔记)(20)

javascript中的原型链(JS面向对象编程笔记)(21)

javascript中的原型链(JS面向对象编程笔记)(22)

Foo里面的constructor是继承而来的,继承的当前构造函数的原型

javascript中的原型链(JS面向对象编程笔记)(23)

这就是原型链,是不是很绕(我也晕晕的),研究的就是原型对象

1.8、prototype属性的作用

js的继承就是通过prototype来进行的

什么是js的继承机制?

<body> <script> function Foo() {} var f1 = new Foo(); Foo.prototype.name = "chen"; console.log(f1.name); </script> </body>

看结果:

javascript中的原型链(JS面向对象编程笔记)(24)

javascript中的原型链(JS面向对象编程笔记)(25)

可以看到,name属性并不在f1上,而是通过继承挂载在f1的原型对象(__proto__)上

<body> <script> function Foo() {} var f1 = new Foo(); var f2 = new Foo(); Foo.prototype.name = "chen"; console.log(f1.name); console.log(f2.name); Foo.prototype.name = "chen2"; console.log(f1.name); console.log(f2.name); </script> </body>

javascript中的原型链(JS面向对象编程笔记)(26)

可以看到两个实例都会被修改

JS继承机制,通过原型对象(__proto__)实现继承。原型对象的作用,就是定义了所有的实例对象共享的属性和方法

读取属性和方法的规则:js引擎会先寻找对象本身的属性和方法,如果找不到就到它的原型对象去找,如果还是找不到,就到原型的原型去找,如果直到最顶层的Object.prototype还是找不到,就会返回undefined 如果对象和它的原型,都定制了同名的属性,那么优先读取对象自身的属性,这也叫覆盖

<body> <script> function Foo() {} var f1 = new Foo(); var f2 = new Foo(); Object.prototype.showName = function(){ console.log(24); } f1.showName() var arr = [12,3,4] arr.showName() </script> </body>

javascript中的原型链(JS面向对象编程笔记)(27)

所有的对象的尽头都是Object

接下来我们编写一段代码,让我们自己的构造函数拥有数组的部分属性和方法:

<body> <script> function MyArray() {} MyArray.prototype = Array.prototype; var arr = new MyArray(); console.log(arr); arr.push(1,2,3) console.log(arr); </script> </body>

javascript中的原型链(JS面向对象编程笔记)(28)

但是上面的代码同时也改变了他的constructor,修改成了数组的constructor,怎么样能不改变构造函数的指向呢

要添加这一句:

MyArray.prototype.constructor = MyArray;

我们打印一下arr.constructor

javascript中的原型链(JS面向对象编程笔记)(29)

总结:一旦我们修改构造函数的原型对象,为防止引用出现问题,同时也要修改原型对象的constructor属性

constructor属性表示原型对象和构造函数之间的关联关系

1.9、总结

function Foo(){}; var f1 = new Foo();

构造函数:Foo

实例对象:f1

原型对象:Foo.prototype

1.原型对象和实例对象的关系

console.log(Foo.prototype === f1.__proto__);

javascript中的原型链(JS面向对象编程笔记)(30)

2.原型对象和构造函数的关系

console.log(Foo.prototype.constructor === Foo);

javascript中的原型链(JS面向对象编程笔记)(31)

3.实例对象和构造函数

间接关系是实例对象可以继承远程对象的constructor属性

console.log(f1.constructor === Foo);

Foo.protptype={}; console.log(Foo.prototype === f1.__proto__); console.log(Foo.prototype.constructor === Foo);

javascript中的原型链(JS面向对象编程笔记)(32)

所以,代码的顺序很重要

javascript中的原型链(JS面向对象编程笔记)(33)

,