JavaScript进阶之路之面向对象编程 详解(二)

一、 原型对象与原型链

1.1 构造函数的缺点

之前我们自定义对象时,以构造函数为模板;
对象的属性和方法,可以定义在构造函数内部。

JavaScript进阶之路之面向对象编程 详解(二)_第1张图片

 

 

以上代码其中的颜色重复,如果我们将颜色拿出来,放在公用的地方来实现属性共享,节省内存;

1.2 prototype 属性

JavaScript 的每个对象都继承另一个对象,父级对象称为“原型”(prototype)对象。
只有null除外,它没有自己的原型对象。
而原型对象上的所有属性和方法,都能被派生对象共享
通过构造函数生成实例对象时,会自动为实例对象分配原型对象。
每一个
构造函数都有一个prototype属性,这个属性就是实例对象的原型对象。

JavaScript进阶之路之面向对象编程 详解(二)_第2张图片

 

原型对象的属性不是实例对象自身的属性。
但是只要修改原型对象,变动就立刻会体现在所有实例对象上

如果实例对象自身就有某个属性或方法,那么原型对象上的属性和方法便会失效;

JavaScript进阶之路之面向对象编程 详解(二)_第3张图片

 

总结一下,原型对象的作用,就是定义所有实例对象共享的属性和方法。
这也是它被称为原型对象的原因,而实例对象可以视作从原型对象衍生出来的子对象。

由于 JavaScript 的所有对象都有构造函数(只有null除外),
而所有构造函数都有prototype属性(其实是所有函数都有prototype属性),所以所有对象都有自己的原型对象。

1.3 原型链

对象的属性和方法,有可能是定义在自身内,也有可能是定义在它的原型对象上。
由于原型本身也是对象,又有自己的原型,所以形成了一条
原型链(prototype chain)。
比如,a对象是b对象的原型,b对象是c对象的原型,以此类推。

如果一层层地上溯,所有对象的原型最终都可以上溯到Object;
那么,Object对象有没有它的原型呢?
有的,就是null,而null对象没有自己的原型。

对象.__proto__ (两边都是两个下划线):获取对象的原型对象

JavaScript进阶之路之面向对象编程 详解(二)_第4张图片

原型链的作用是,读取对象的某个属性时,JavaScript 引擎先寻找对象本身的属性,
如果找不到,就到它的原型去找,如果还是找不到,就到原型的原型去找。
如果直到最顶层的Object.prototype还是找不到,则返回undefined。
如果对象自身和它的原型,都定义了一个同名属性,那么优先读取对象自身的属性,这叫做“覆盖”(overriding)。

需要注意的是,一级级向上,在原型链寻找某个属性,对性能是有影响的。
如果寻找某个不存在的属性,将会遍历整个原型链。

注意:
在实际项目开发中,我们通常会使用第三方框架(一个类),但是当我们发现这个类
中并不存在我们想要属性或方法时,不能直接修改源代码,
但是可以通过原型对象的添加我们自己的属性或方法。

1.4 原型操作

1.4.1 constructor 属性

对象有一个constructor属性,指向原型对象所在的构造函数;

JavaScript进阶之路之面向对象编程 详解(二)_第5张图片

1.4.2 设置获取原型对象

Object.getPrototypeOf() 方法返回一个对象的原型对象。
这是获取原型对象的标准方法。

JavaScript进阶之路之面向对象编程 详解(二)_第6张图片

 

Object.setPrototypeOf() 为现有对象设置原型对象
第一个是现有对象,第二个是要设置成为原型对象的对象
这是设置原型对象的标准方法。

__proto__ 属性:
前面用proto来获取原型对象,如果给proto属性赋值,则是设置原型对象;
但是,不建议使用,而是用Object.getPrototypeof()(读取)和Object.setPrototypeOf()(设置),进行原型对象的读写操作

JavaScript进阶之路之面向对象编程 详解(二)_第7张图片

 

JavaScript进阶之路之面向对象编程 详解(二)_第8张图片

1.4.3 获取原型对象方法及比较

通过上面的学习,我们知道,获取实例对象obj的原型对象,有三种方法。

> obj.__proto__

> obj.constructor.prototype

> Object.getPrototypeOf(obj)

上面三种方法之中,前两种都不是很可靠。
最新的ES6标准规定,proto属性只有浏览器才需要部署,其他环境可以不部署。
而obj.constructor.prototype在手动改变原型对象时,会失效。

推荐使用第三种Object.getPrototypeOf方法,获取原型对象。

三、 闭包

2.1 闭包的概念

JavaScript进阶之路之面向对象编程 详解(二)_第9张图片

JavaScript有两种作用域:全局作用域和函数作用域。函数内部可以直接读取全局变量。
函数 f1 可以读取全局变量 n。
但是,在函数外部无法读取函数内部声明的变量。

JavaScript进阶之路之面向对象编程 详解(二)_第10张图片

但是,有时我们却需要在函数外部访问函数内部的变量;
正常情况下,这是办不到的,只有通过变通方法才能实现。
那就是在函数的内部,再定义一个函数。

function f1() {

    var n = 999;

    var f2 = function() {

        console.log(n);

    }

return f2;

}

var f = f1();

f();

上面代码中,函数f2就在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。
但是反过来就不行,f2内部的局部变量,对f1就是不可见的。
这就是JavaScript语言特有的”链式作用域”结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。
所以,父对象的所有变量,对子对象都是可见的,反之则不成立。

既然f2可以读取f1的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!

闭包就是函数f2,即能够读取其他函数内部变量的函数。
由于在JavaScript语言中,只有函数内部的子函数才能读取内部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”。
闭包最大的特点,就是它可以“记住”诞生的环境,比如f2记住了它诞生的环境f1,所以从f2可以得到f1的内部变量。
在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁;

2.2 垃圾回收机制及闭包

function f1() {

    var n = 99;

    console.log(++n);

}

f1(); //100

f1(); //100

当我们在函数内部引入一个变量或函数时,系统都会开辟一块内存空间;
还会将这块内存的引用计数器进行初始化,初始化值为0
如果外部有全局变量或程序引用了这块空间,则引用计数器会自动进行+1操作
当函数执行完毕后,变量计数器重新归零,系统会运行垃圾回收机制,将函数运行产生的数据销毁;
如计数器不是 0 ,则不会清楚数据;
这个过程就称之为 "JS的垃圾回收机制" 

JavaScript进阶之路之面向对象编程 详解(二)_第11张图片

闭包的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量始终保持在内存中,
即闭包可以使得它诞生环境一直存在;

注意,外层函数每次运行,都会生成一个新的闭包,而这个闭包又会保留外层函数的内部变量,所以内存消耗很大。
因此不能滥用闭包,否则会造成网页的性能问题。

三、call或apply方法

回顾this的指向:
全局作用域下,this执向window对象;
构造函数中,this指向实例化对象;

我们能不能在调用函数时直接修改函数内部的this指向呢?
答:使用callapply方法

3.1 call方法
函数名称.call(obj,arg1,arg2...argN);
参数说明:
obj:函数内this要指向的对象
arg1,arg2...argN :参数列表,参数与参数之间使用一个逗号隔开JavaScript进阶之路之面向对象编程 详解(二)_第12张图片

 

3.2 apply方法

函数名称.apply(obj,[arg1,arg2...,argN])
参数说明:
obj this要指向的对象
[arg1,arg2...argN] : 参数列表,但是要求格式为数组

JavaScript进阶之路之面向对象编程 详解(二)_第13张图片

 

两者的功能完全一致,都是为了改变函数内部的this指向,唯一的区别在于参数传递方式不同
call方法可能有多个参数,第一个为要指向的对象,其他参数为函数的实参;
apply方法最多只能有两个参数,第一个为要指向的对象,第二个为数组,数组内容为函数的实参;

四、 私有属性

PHP类中我们使用private声明私有属性;
public :公有的
protected :受保护的
private :私有的

但是在JS中,只有两种属性,公有属性与私有属性,在构造函数内部通过this声明的属性就是公有属性,通过var声明的就是私有属性。

JavaScript进阶之路之面向对象编程 详解(二)_第14张图片

 

 

五、 对象的继承

PHP中,我们可以使用 extends 关键字来实现类的继承;
但是,JS中并没有类似 extends 的关键字提供继承的功能;
而我们知道,所谓的继承,其实就是为子类提供父类中的方法和属相,使子类能够使用父类中的属性及方法;

1:通过原型实现继承;
2:通过callapply方法继承;

JavaScript进阶之路之面向对象编程 详解(二)_第15张图片

 

JavaScript进阶之路之面向对象编程 详解(二)_第16张图片

 

六、定时器

JavaScript提供定时执行代码的功能,叫做定时器:

setTimeout()
用来指定某个函数或某段代码,在多少毫秒之后执行;
setInterval()
指定某个任务每隔一段时间就执行一次,也就是无限次的定时执行。

setTimeout()setInterval()的第一个参数都是指定要执行的函数名称或者代码段,第二个参数时时间,以毫秒为单位;

clearTimeout()clearInterval()

setTimeoutsetInterval函数,都返回一个表示计数器编号的整数值,
将该整数传入clearTimeoutclearInterval函数,就可以取消对应的定时器。

JavaScript进阶之路之面向对象编程 详解(二)_第17张图片

我是小咖

发个邀请:
如果你正好想学习php,可以与我一起交流,我的VX:feilueze333。下面资料免费赠送

JavaScript进阶之路之面向对象编程 详解(二)_第18张图片

 

你可能感兴趣的:(前端之巅,javascript,编程语言,js,css,html5)