程序语言都有代码复用的机制,类似Java, C#这种面向对象语言采用了继承的方式,换句话说class是object的模板, 子类继承父类, 通过子类创建的object也就有了父类的各种属性和方法
JS采用了原型链的方式,要理解原型链,首先就要放弃class的思维方式(虽然ES6标准引入了class关键字,但是class只是function的语法糖而已,实际底层还是function;)
使用JavaScript的new关键字创建对象
function Person(name, age) {
this.name = name;
this.age = age;
this.sayHi = function() {
console.log(`hi from ${name}`)
}
}
const me = new Person("rz", 24);
console.log(me.age);
me.sayHi()
这里的Person函数的作用很简单,构建对象,什么样的对象呢?这个对象有个name和age属性,有个sayHi的方法,仅此而已。但是JS中函数本身也是个对象,也就是说Person自己是个对象。是对象就可以设定属性:
Person.purpose = "我的目的是构造一个对象,这个对象有name和age属性,还有一个sayHi的方法";
console.log(Person.purpose)
这里给Person这个对象加了个purpose属性, 类型是string. 但这个属性只属于Person, 和Person构建的对象毫无关系
console.log(me.purpose); //undefined
那我们怎么给me加一个purpose的属性呢?当然我们可以在me被创建后手动添加:
me.purpose = "Make the world a better place";
所以每个人的purpose都应该是"Make the world a better place". 也就是说,最好是me被创建时就有purpose属性, 而且它的值是"Make the world a better place", 最直接的做法是修改Person函数:
function Person(name, age) {
this.purpose = "Make the world a better place";
this.name = name;
this.age = age;
this.sayHi = function() {
console.log(`hi from ${name}`)
}
}
这种当然没有问题,但是有些时候Person函数是别人的代码,直接改动别人的实现方式不大好,我们能不能通过扩展的方式实现呢?
Person.prototype.purpose = "Make the world a better place";
const me = new Person('rz', 24);
const you = new Person('jack', 25);
console.log(me.purpose, "|", you.purpose);
这里在求值me.purpose的过程中,JS发现me自己没有purpose这个属性, 它会去me的构造函数Person的prototype里面找(对,就是这么绕), 看有没有purpose属性,也就是Person.prototype有没有purpose这个属性。发现有, JS返回"Make the world a better place";
那如果是没有找到呢?
console.log(me.address);//undefined
JS发现me没有address属性,就去Person.prototype里面找, 发现也没有, 继续往上找, 最上层一定是Object.prototype, 发现也没有, JS放弃,返回undefined