构造函数和原型
# 构造函数和原型
创建对象可以通过以下三种方式
- 对象字面量
var obj = {}
1
- new Object()
var obj = new Object()
1
- 自定义构造函数
# 构造函数
构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与 new 一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面;
# 特点
- 构造函数有原型对象 prototype;
- 构造函数原型对象 prototype 里面有 constructor 指向构造函数本身;
- 构造函数可以通过原型对象添加方法;
- 构造函数创建的实例对象有proto原型指向构造函数的原型对象;
# new 在执行时会做四件事情:
- 在内存中创建一个新的对象;
- 让 this 指向这个新的对象;
- 执行构造函数里面的代码,给这个新对象添加属性和方法;
- 饭后这个新对象(所以构造函数里面不需要 return)
JavaScript 构造函数中可以添加一些成员,可以在构造函数本身上添加,也可以在构造函数内部的 this 上添加。通过这两种方式添加的成员,就分别称为静态成员和实例成员
- 静态成员:在构造函数上添加的成员称为
静态成员,只能由构造函数本身来访问。 - 实例成员:在构造函数内部创建的对象成员称为
实例成员,只能由实例化的对象来访问。实例成员就是构造函数内部通过 this 添加的成员;
# 原型
- 构造函数通过原型分配的函数是所有对象所共享的
- JavaScript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象。注意这个 prototype 就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。
- 我们可以把那些不变的方法,直接定义在 prototype 对象上,这样所有对象的实例就可以共享这些方法。
# 继承
ES6 之前并没有我们提供 extends 继承。我们可以通过构造函数+原型对象模拟实现继承,被称为组合继承
# call
调用这个函数,并且修改函数运行时的 this 指向
fun.call(thisArg, arg1, arg2, ...)
1
# apply
fun.apply(thisArg, [array])
1
# 主要应用
比如说,我们可以利用 apply 借助于数学内置对象求最大值;
var arr = [1, 23, 43, 12, 4, 11]
Math.max.apply(null, arr)
1
2
2
# bind
不会调用函数。但是能改变内部 this 指向;
fun.bind(thisArg, arr1, arr2 , ...)
1
- thisArg:在 fun 函数运行时指定的 this 值;
- arg1、arg2:传递的其它参数;
- 返回由指定的 this 值和初始化参数改造的原函数拷贝
var o = {
name: 'coderly',
}
function fn() {
console.log(this)
}
var f = fn.bind(o)
f()
// 1. 不会调用原来的函数,可以改变原来函数内部的this指向;
// 2. 返回的是原函数改变this之后产生的新函数;
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# bind 应用
如果有的函数我们不需要立即调用,但是又想改变这个函数内部的 this 指向,此时用 bind
【例子】:我们有一个按钮,当我们点击了之后,就禁用这个按钮,3 秒钟之后开启这个按钮;
var btn = document.querySelector('btn')
btn.onclick = function() {
this.disabled = true
setTimeout(
function() {
this.disabled = false
}.bind(this),
3000
)
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 主要应用场景
- call 经常做继承;
- apply 经常跟数组有关系,比如借助于数学对象实现数组最大值最小值;
- bind 不调用函数,但是还想改变 this 指向,比如改变定时器内部的 this 指向;
# 借用构造函数继承父类型属性
核心原理:通过 call()把父类型的 this 指向子类型的 this,这样就可以实现子类型继承父类型的属性;
// 父构造函数
function Father(name, age) {
this.name = name
this.age = age
}
Father.prototype.money = function() {
console.log('我有1000元钱')
}
// 子构造函数
function Son(name, age) {
// 继承Father内部属性
Father.call(this, name, age)
}
// 继承Father原型上的方法
// 如果利用对象的形式修改了原型对象,别忘了利用constructor指回原来的原型对象;
Son.prototype = new Father()
Son.prototype.constructor = Son
// Son的自己的方法,必须在上一步之后
Son.prototype.Mine = function() {
console.log('我私藏了100元钱')
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
【注意】:如果利用对象的形式修改了原型对象,别忘了利用 constructor 指回原来的原型对象;
# 类
# 本质
- class 本质还是一个 function;
- 类的所有方法都定义在类的 prototype 原型对象;
- 类创建的实例,里面也有proto指向类的 prototype 原型对象;
- 所以 ES6 的类它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已;
- 所以 ES6 的类其实就是语法糖;
- 语法糖:语法糖就是一种便捷写法,简单理解,有两种方法可以实现桐乡的功能,但是一种写法更加清晰、方便,那么这个方法就是语法糖;
# 特点
- 类的本质其实还是一个函数,我们也可以简单的认为,类就是构造函数的另一种写法;
- 类有原型对象 prototype;
- 类原型对象 prototype 里面有 constructor 指向类本身;
- 类可以通过原型对象添加方法;
上次更新: 2021/09/13, 15:11:59