通过构造函数创建对象
构造函数是学习面向对象的基础
任何函数都有原型对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
function Pig() {
// this表示实例对象,本例中表示obj
this.name = '佩奇' //obj.name='佩奇'
this.age = 20
// this.say = function () {
// console.log(this.name + '我是佩奇')
// }
// this.eat = function () {
// console.log('吃饭')
// }
}
let obj1 = new Pig('乔治', 2)
let obj3 = new Pig('老段', 4)
// 当前问题,没创建一个对象就创建一个say和eat,但是多个对象中的say和eat相同,没必要存多个
//解决方法,创建原型对象,相同的东西存一份
//任何一个函数(普通函数,构造函数,内置的Array、String)都有原型对象,由系统分配,不用手动添加
//console.dir()也是输出内容,而且输出后可以展开看到内部的结构
console.dir(Pig) //Pig.prototype就是Pig的原型对象
console.log(Pig)
console.log('--------------------')
// 把构造函数中的eat、say方法,加给原型对象
Pig.prototype.say = function () {
console.log('会说话')
}
console.log(obj1)
console.log(obj3)
</script>
</body>
</html>
扩展内置构造函数
<script>
let arr = [2, 3, 5, 8]
// console.log(typeof (arr)) //object
// 数组为什么是对象
// [2, 3, 5, 8]是字面量写法,实际上这个数组也是通过new Array()得来的
// 内部实现 [2, 3, 5, 8]===new Array(2, 3, 5, 8)
Array.prototype.sum = function () {
// this表示实例对象,本利中的this表示[2,3,5,8]
return this.reduce((t, c) => t + c)
}
console.log(arr.sum()) //调用原型对象定义的方法 //18
// 给原型对象加方法的好处,任何实例对象都可以调用
</script>
构造函数、原型对象、实例对象三者之间的关系
<script>
function Pig(name, age) {
this.name = name
this.age = age
}
// 向原型对象上添加方法
Pig.prototype.say = function () { }
Pig.prototype.eat = function () { }
console.log(Pig.prototype.constructor) //Pig是构造函数:Pig.prototype找到原型对象:再点constructor再次找到构造函数
// 实例化
let obj = new Pig('佩奇', 3)
console.log(obj.__proto__) //通过实例对象找到原型对象(非标准) _,_proto_,_
//其实,实例对象的[[Prototype]]属性才指向原型对象
// 只不过,[[Protype]]是隐藏属性,不可访问,索引浏览器才实现了__proto__方法,用于访问原型对象
// 但是,__proto__是js的非标准属性并且可能遗弃
// 建议使用Object.getPrototypeOf(实例对象)语法,来找原型对象
</script>
原型继承
让Animal的实例对象成为Pig的原型对象,那么Pig的实例对象,可以使用Pig原型对象上的方法,也可以使用Animal原型对象上的方法
function Pig(name, age) {
this.name = name
this.age = age
}
// 修改Pig的原型对象为Animal的实例对象
Pig.prototype = a
let p = new Pig()
// 因为修改了Pig的原型对象===Animal的实例对象,所以实例对象p就继承了Animal的实例对象a的属性和方法
p.eat()
// 通过原型继承后,需要手动修改原型对象的constructor属性,否则就不符合上述三角关系了
// 要不然Pig的constructor将会指向Animal
// 只要修改了原型对象,就得手动指定constructor属性
// 固定语法:构造函数.prototype.constructor=构造函数,
console.log(Pig.prototype.constructor)
Pig.prototype.constructor = Pig
console.log(Pig.prototype.constructor)
</script>
原型链
基于原型对象的继承使得不同构造函数的原型对象关联在一起,并且这种关联的关系是一种链式结构,我们将原型对象的链状结构关系称为原型链
研究对象属性和方法的查找过程
对象属性(或方法)查找机制
a)当查找对象的某个属性(包括方法)时,优先从当前对象自身中查找
b)当前对象中没有该属性,则向上层原型对象中查找
c)如果原型对象中也没有该属性则继续向上层原型对象中查找
d)…
f)一直查找到最顶层位置,找到则使用该属性,找不到则得到null
Object.prototype是最顶层的原型对象
Array、String等的原型对象也是它
instanceof 运算符
语法:
实例对象 instanceof 构造函数
instanceof用于检测实例对象的原型链上,是否有语法中给出的构造函数
<script>
function Animal() {
}
Animal.prototype.eat = function () {
console.log('会吃饭')
}
let a = new Animal()
// -------------------------------------------Pig
function Pig(name, age) {
this.name = name
this.age = age
}
// 修改Pig的原型对象为Animal的实例对象
Pig.prototype = a
let p = new Pig()
// 因为修改了Pig的原型对象===Animal的实例对象,所以实例对象p就继承了Animal的实例对象a的属性和方法
console.log(p instanceof Animal)
console.log(p instanceof Object)
console.log(p instanceof Pig)
</script>