JavaScript 基础
Document
运算符
深入数据和类型
函数进阶
原型、继承
类
浏览器存储
Web API
事件
错误处理
异步编程
网络请求
模块
练习
实例
工具与规范
软件架构模式
设计模式
函数的 prototype
之前学到过,通过 new Function()
的方式来创建一个对象。
假如 Function.prototype
为一个对象,那么使用 new
创建一个对象时将会把 Function.prototype
作为新对象的 [[Prototype]]
。
$$jsdemo$$
$$edit$$
const animal = {
sleep() {
alert("睡觉")
},
}
function Cat() {
this.name = "猫猫"
}
Cat.prototype = animal
const cat = new Cat()
// 隐性执行了 cat.__proto__ = Cat.prototype
cat.sleep() // 睡觉
alert(cat.__proto__ === animal) // true
函数有一个默认的 prototype
,prototype
为一个对象且只有一个属性 constructor
指向函数自身。
$$jsdemo$$
$$edit$$
function Cat() {}
console.log(Cat.prototype) // {constructor: Cat}
console.log(Cat.prototype.constructor === Cat) // true
$$warning
不建议使用 Function.prototype = { ... }
这种写法,因为会将函数自身的 prototype
完全覆盖掉。推荐使用 Function.prototype.xxx = xxx
的写法。
$$
$$jsdemo$$
$$edit$$
function Cat(name) {
this.name = name
}
// 不推荐
Cat.prototype = {
sayName() {
alert(`我叫${this.name}`)
},
}
const cat = new Cat("喵喵")
// cat 的 constructor 消失了
// 这里打印的是更上一层原型 Object 的 constructor
console.log(cat.constructor) // Object() { }
function Dog(name) {
this.name = name
}
// 推荐
Dog.prototype.sayName = function sayName() {
alert(`我叫${this.name}`)
}
const dog = new Dog("狗狗")
// dog 的 constructor 仍然可以找到
console.log(dog.constructor) // Dog() { }
$$tip
构造函数中的方法、属性尽量写到 prototype
中,便能得到上章提到的原型继承的好处。
$$
$$jsdemo$$
$$edit$$
function Cat(name) {
this.name = name
// 不推荐
this.sayName = function () {
alert(`我叫${this.name}`)
}
}
const cat = new Cat("喵喵")
const cat2 = new Cat("丑丑")
const cat3 = new Cat("加菲")
console.log(cat)
console.log(cat2)
console.log(cat3)
如控制台看到的一样,每个实例都有一个重复的 sayName
方法。
以下是把 sayName
定义到了 prototype
中。
$$jsdemo$$
$$edit$$
function Cat(name) {
this.name = name
}
// 推荐
Cat.prototype.sayName = function () {
alert(`我叫${this.name}`)
}
const cat = new Cat("喵喵")
const cat2 = new Cat("丑丑")
const cat3 = new Cat("加菲")
console.log(cat)
console.log(cat2)
console.log(cat3)
实例共用了原型上的 sayName
方法。
instanceof
我们一定用过 instanceof
这个操作符,这个操作符的原理便是判断函数的 prototype
是否存在于实例的原型链上。
比如以下的实例。
$$jsdemo$$
$$edit$$
function Cat() {
this.name = "猫猫"
}
function Garfield() {
this.name = "加菲猫"
}
Garfield.prototype.__proto__ = Cat.prototype
const garfield = new Garfield()
alert(garfield instanceof Garfield) // true
// 因为 garfield.[[Prototype]] === Garfield.prototype
alert(garfield.__proto__ === Garfield.prototype) // true
alert(garfield instanceof Cat) // true
// 因为 garfield.[[Prototype]].[[Prototype]] === Cat.prototype
alert(garfield.__proto__.__proto__ === Cat.prototype) // true
function Dog() {
this.name = "狗狗"
}
alert(garfield instanceof Dog) // false
原生类型的 prototype
当我们创建一个对象、数组、函数等等原生对象时,相当于调用了对应的构造函数。
$$jsdemo$$
$$edit$$
// 以下是等价的
const obj = {}
const obj2 = new Object()
alert(obj.__proto__ === Object.prototype) // true
// 以下是等价的
const arr = []
const arr2 = new Array()
alert(arr.__proto__ === Array.prototype) // true
这意味着我们很多自带的方法其实是在 prototype
中实现的。
$$jsdemo$$
$$edit$$
// 比如数组常用的 join 方法就在 prototype 中
const arr = [1, 2, 3]
alert(arr.join === Array.prototype.join) // true
alert(arr.join("-")) // 1-2-3
这表示我们可以自己实现一些方法,并挂载到 prototype
中。
$$jsdemo$$
$$edit$$
String.prototype.toNumber = function () {
return Number(this)
}
const n = "123".toNumber()
alert(n) // 123
alert(typeof n) // number
练习
- 补全以下代码,使用到
prototype
,并使其能正常运行。
function MathArray(...arr) {
this.arr = arr
}
// 补全这里
// MathArray.prototype...
const arr = new MathArray(5, -10, 20, 12)
alert(arr.getMin()) // -10
alert(arr.getMax()) // -20
$$answer
$$jsdemo$$
$$edit$$
function MathArray(...arr) {
this.arr = arr
}
// 补全这里
MathArray.prototype.getMin = function () {
return this.arr.reduce((a, b) => (a < b ? a : b))
}
MathArray.prototype.getMax = function () {
return this.arr.reduce((a, b) => (a > b ? a : b))
}
const arr = new MathArray(5, -10, 20, 12)
alert(arr.getMin()) // -10
alert(arr.getMax()) // -20
$$
- 实现一个
myInstanceof
函数,接收两个参数实例和构造函数,判断实例是否是构造函数的实例。
function myInstanceof(obj, func) {
// 补全这里
}
function Cat() {
this.name = "猫猫"
}
function Garfield() {
this.name = "加菲猫"
}
Garfield.prototype.__proto__ = Cat.prototype
const garfield = new Garfield()
alert(myInstanceof(garfield, Garfield)) // true
alert(myInstanceof(garfield, Cat)) // true
function Dog() {
this.name = "狗狗"
}
alert(myInstanceof(garfield, Dog)) // false
$$answer
$$jsdemo$$
$$edit$$
function myInstanceof(obj, func) {
if (obj.__proto__ === func.prototype) {
// 找到原型
return true
}
if (obj.__proto__ === null) {
// 到顶
return false
}
return myInstanceof(obj.__proto__, func)
}
function Cat() {
this.name = "猫猫"
}
function Garfield() {
this.name = "加菲猫"
}
Garfield.prototype.__proto__ = Cat.prototype
const garfield = new Garfield()
alert(myInstanceof(garfield, Garfield)) // true
alert(myInstanceof(garfield, Cat)) // true
function Dog() {
this.name = "狗狗"
}
alert(myInstanceof(garfield, Dog)) // false
$$
- 补全以下代码,使得数组自带
getSum
方法对所有数字求和。
// 补全以下代码
const arr = [5, 10, 20]
alert(arr.getSum()) // 35
$$answer
$$jsdemo$$
$$edit$$
Array.prototype.sum = function () {
return this.reduce((a, b) => a + b)
}
const arr = [5, 10, 20]
alert(arr.getSum()) // 35
$$