概述
这是我的学习笔记,记录了JavaScript的学习过程。在写博客的时候我会尽量详尽的记录每个知识点。如果你完全没接触过JavaScript,那么这一系列的学习笔记可能会对你有所帮助。
今天继续学习对象,主要是Object.create()、原型链、修改原型指向等。
1.Object.create()
Object.create()可以让一个对象继承自另一个对象,新的对象拥有它继承对象的所有属性,并且还能保留自己的特有属性,如下代码示例:
// 创建构造函数
function Student(name, id) {
this.name = name;
this.id = id;
this.eat = function () {
console.log(this.name + "吃饭");
};
}
//创建实例对象stu1
var stu1 = new Student("小慈", 20240001);
console.log(stu1);
//遍历对象stu1的属性
for (key in stu1) {
console.log(key);
}
//创建一个对象,继承自stu1
var monitor = Object.create(stu1);
console.log(monitor); //Student {}
如上代码,我们创建了一个monitor对象,继承自stu1。上面代码stu1的属性遍历结果是如下:
name
id
eat
我们再遍历一下monitor的属性,如下代码:
//遍历对象monitor的属性
for (key in monitor) {
console.log(key);
}
monitor属性遍历的结果如下:
name
id
eat
从上我们可以看出,monitor属性与stu1一致,它的属性完全继承自stu1.
用Object.getOwnPropertyNames可以获取对象自己的属性,我们分别获取一下stu1和monitor的自己的属性,如下代码:
console.log(Object.getOwnPropertyNames(stu1)); //(3) ['name', 'id', 'eat']
console.log(Object.getOwnPropertyNames(monitor)); //[]
我们可以看到stu1是有三个自己的属性,monitor没有自己的属性,因为它是继承自stu1的,所以没有自己的属性,我们试一下打印一下monitor的name属性值,如下:
console.log(monitor.name); //小慈
它的属性值跟stu1一样,我们接下来给monitor修改属性,如下代码:
//修改属性
monitor.name = "小会";
monitor.id = 20240010;
console.log(monitor); //Student {name: '小会', id: 20240010}
//获取对象自己的属性
console.log(Object.getOwnPropertyNames(monitor)); //(2) ['name', 'id']
我们看到修改了 monitor对象中name和id的属性值,它就有的自己的属性,我们再调用一下eat方法试试:
//调用eat方法
monitor.eat(); //小会吃饭 说明this指向了monitor
可以看到eat方法运行结果是小会吃饭,不是小慈吃饭,说明this指向了monitor。
2. 原型链
JavaScript中的每个对象都有一个内部链接指向它的原型对象。当试图访问一个对象的属性时,如果该对象自身没有这个属性,那么JavaScript会在对象的原型上查找这个属性,这就是原型链。如果原型对象自身也没有这个属性,那么会继续在原型对象的原型上查找,以此类推,直到找到属性或者到达原型链的末尾(通常是null)。
在JavaScript中,几乎所有的对象都继承自Object.prototype。Object.prototype是所有对象的最终原型。这意味着,如果你尝试访问一个对象上不存在的属性或方法,JavaScript最终会在Object.prototype上查找。如果Object.prototype上也没有找到,那么会返回undefined。
简单来说就是每个对象的原型都会有一个上层的原型,直到遇到null,这种链式继承下来的原型就构成了原型链,JavaScript中最顶层的对象是Object,它的原型是Object.prototype,但是它的原型的原型就是null,这样就达到了原型链的顶端。
下面代码演示一下,下面代码是获取对象monitor(monitor是我们上一个小节中定义的对象)的原型,并赋值给变量protoOfMonitor:
//Object.getPrototypeOf获取对象的原型
var protoOfMonitor = Object.getPrototypeOf(monitor);
console.log(protoOfMonitor);
上面代码运行结果如下,因为monitor继承自stu1,所以它的原型指向的是stu1。
我们进一步获取protoOfMonitor的原型,并赋值给变量protoOfStu1,如下代码示意:
var protoOfStu1 = Object.getPrototypeOf(protoOfMonitor);
console.log(protoOfStu1);
运行结果如下图所示,因为stu1是用构造函数创建的,所以它的原型指向的是构造函数:
我们再进一步获取 protoOfStu1的原型,并赋值给变量protoOfStudent,如下代码所示:
var protoOfStudent = Object.getPrototypeOf(protoOfStu1);
console.log(protoOfStudent);
运行结果如下,因为构造函数的原型是Object对象,所以它的原型指向的是Object:
到这一层已经到了Object对象了,我们再进一步获取protoOfStudent的原型,如下代码:
var protoOfObj = Object.getPrototypeOf(protoOfStudent);
console.log(protoOfObj);
运行结果为:null
至此,我们已经达到了原型链的最顶端了。这就是对象的原型链。
3.修改原型指向
修改原型的指向,可以改变对象的继承关系,从而获取不同的属性和方法。这通常是通过改变构造函数的prototype属性来实现的。如下代码示例:
function Person() {}
//给Person添加原型方法
Person.prototype.sayHello = function () {
console.log("你好,我是" + this.name);
};
//给Person添加原型属性
Person.prototype.species = "人类";
//定义一个构造函数Students
function Students(name, id) {
this.name = name;
this.id = id;
this.sayGood = function () {
console.log("我很好!");
};
}
//创建实例对象stu2
var stu2 = new Students("小雪", 20240030);
console.log(stu2.name, stu2.id); //可以访问name和id属性,输出:小雪 20240030
stu2.sayGood(); //调用sayGood方法,输出:我很好!
console.log(stu2.species); //没有species,返回:undefined
//stu2.sayHello();
//调用sayHello方法,运行报错:Uncaught TypeError: stu2.sayHello is not a function
//查看stu2的原型
console.log(Object.getPrototypeOf(stu2));
上面的例子中,我们先创建了一个Person构造函数,并给他增加了原型方法sayHello和原型属性species,我们再创建一个Students构造函数并创建实例对象stu2,我们访问stu2的name和id的属性都能访问,stu2也可以调用sayGood方法,说明stu2继承了构造函数的属性和方法,我们访问stu2的species属性时,会返回undefined,调用stu2的sayHello方法时会报错,因为species属性sayHello方法是Person构造函数的原型属性和方法,stu2与Person构造函数没有继承关系。我们再查看stu2的原型,返回的是如下的结果,可以看出来,stu2的原型指向的是Students构造函数。
接下来我们来修改原型指向,让stu2也具有species属性sayHello方法,并且我们再访问之前的那些属性和方法,如下代码:
//修改stu2的原型指向
Object.setPrototypeOf(stu2, Person.prototype);
console.log(stu2.name, stu2.id); //依然可以访问name和id属性,输出:小雪 20240030
stu2.sayGood(); //调用sayGood方法,输出:我很好!
stu2.sayHello(); //调用sayHello方法,输出:你好,我是小雪
console.log(stu2.species); //输出:人类
我们可以看到,修改原型指向后,stu2具有了species属性sayHello方法,并且还保留了原来的属性和方法,我们再看一下现在的stu2的原型,如下代码:
//查看stu2的原型
console.log(Object.getPrototypeOf(stu2));
运行结果如下:
可以看到,stu2的原型指向已经发生改变。最后我们再用绝对等于判断一下stu2的原型与Person的原型是否相等。如下代码:
//判断stu2的原型与Person的原型是否相等
console.log(Object.getPrototypeOf(stu2) === Person.prototype); //true
可以看到返回了true,说明他们是完全相等的。
以上便是今天的学习内容,如果对你有所帮助,请点个赞再走吧。