引言:
今天刚拿到小黄书,就翻到了第二部分,别误会哦!我看的是你不知道的JavaScript。我发现了有一种特别的“三角关系”,它既简单又复杂,既直接又微妙。这三者分别是构造函数、原型对象和实例对象。本文将带你解密这暧昧的三角关系,以及它们如何共同构成了JavaScript面向对象的核心。
我们先来学学如何‘造对象’
对象字面量
let cao = {
name: '张三',
};
let fan = {
name: "王五",
age: "18"
};
在JavaScript中,最简单的创建对象的方式就是使用对象字面量。这种方式直接、明了,但缺乏灵活性和复用性。那么我们可以思考一下怎么批量造!
ES6 Class:现代的面向对象
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
eat() {
console.log(this.name + '在吃饭');
}
}
let lisi = new Person('李四', 18);
let agou = new Person('阿狗', 18);
随着ES6的推出,JavaScript引入了class关键字,使面向对象编程更加直观。class实际上是对构造函数和原型的一种语法糖封装,简化了代码的编写.那把我们的思绪回到推出ES6之前呢?
构造函数:面向对象的传统实现
function Person(name, age) {
this.name = name;
this.age = age;
}
const wen = new Person('雯', 18);
console.log(wen.age, wen.name);
在ES6之前的版本中,JavaScript通过构造函数来实现面向对象。构造函数首字母大写是一种编程习惯,帮助开发者区分普通函数与构造函数。构造函数通过new关键字创建实例对象。
对构造函数的理解
构造函数和普通函数如何区分?
- 构造函数一般是要首字母大写的,但并不意味你大写了,它就是构造函数,这只是一个建议!便于区分于普通函数和构造函数的作用。其实普通函数和构造函数是一样的,重点是函数的调用方式。
- 该函数是否是构造函数,其实取决于你使用了
new去调用,new是一个运算符。 - 例如下面的示例中,
Person('昌',19),没有使用new,这时就引了this关键字,只有你使用了new,this中的指针才会去指向实例化的对象,当你没有new对象时,指针不发生移动也就是指向window。 - 示例:
// 一定是构造函数吗? 不一定
function Person(name,age){
console.log(this);
this.name=name;
this.age=age;
}
Person('昌',19)// 普通函数运行 this 指向 window
const dys=new Person('昌',19);// 构造函数运行 this 指向 实例化的对象 dys
const dyf=new Person('威',20);//
流程:
this 指针 指向 -> 实例对象 ->new 出来的对象
总结:
new就像老板,指示this去完成工作。有了new的指令,this指针才会指向实例化对象,完成属性的赋值,如此一来实例化对象也就生成了。
原型的引出
通过上面的内容,我们可以看到ES6中的语法,属性和方法都分装在了一起,一个类中,而之前的语法中,我还没有提及方法如何设计,这就到了第三者原型登场的了。
原型:共享的秘诀
原型是JavaScript面向对象的一个核心概念。每个函数都有一个prototype属性,指向一个对象,这个对象包含了所有实例共享的属性和方法。通过修改原型对象,我们可以让所有实例共享某些功能,从而提高性能。
function Person(name,age){
console.log(this);
this.name=name;
this.age=age;
}
// 每个函数都有一个原型对象
Person.prototype={
eat: function(){
console.log(`${this.name}爱吃`);
},
sleep: function(){
console.log(`${this.name}爱睡`);
}
}
const K =new Person('KK',18);
K.eat();
K.sleep();
解释:
简单来说,当你就函数实现了一个抽象类Person时,它就具有了.prototype原型对象,你可以在这里面添加方法,
如此之后, 你实例化的对象,它就业具有这个方法,并且可以使用。如上面的K.eat(); K.sleep();
原型式面向对象:独特的设计哲学
JavaScript的面向对象设计哲学与传统的类式继承不同,它基于原型。这种设计哲学类似于中国文化中以孔子为原型的思想传承,而不是基于血缘关系。通过将函数对象的prototype设置为另一个对象,可以实现方法的共享,这是JavaScript原型式面向对象的强大之处:
const cc={
name:"cc",
palyBasketball:function(){
console.log(`${this.name}打篮球`);
},
palypingpang:function(){
console.log(`${this.name}打乒乓`);
}
}
function Person(name,age){
console.log(this);
this.name=name;
this.age=age;
}
// 原型?
Person.prototype=cc;
const wu=new Person('武',20);
wu.palyBasketball();
wu.palypingpang();
//结果:
{}
武打篮球
武打乒乓
console.log(wu.__proto__==cc);
//结果:true
解释:
首先我们定义了cc对象,赋予了它name属性和两个方法,然后用函数构造了一个抽象对象Person,接着Person.prototype=cc;这一步很重要!
- 这行代码的关键在于将
Person构造函数的prototype属性设置为cc对象。这意味着所有通过Person构造函数创建的实例对象都会共享cc对象上的方法。这样实例化的wu,就能实现cc中的两个方法。
谈谈为什么可以调用原型上的方法?
重点在于这部分:
console.log(wu.__proto__==cc);
//结果:true
- 原型链查找机制:当尝试访问
wu实例对象上的playBasketball或playPingPong方法时,JavaScript 引擎会首先在wu对象本身查找这些方法。如果找不到,引擎会沿着[[Prototype]]链向上查找。 wu.__proto__指向cc:由于Person.prototype被设置为cc,所以wu.__proto__就是cc。因此,当在wu上查找playBasketball或playPingPong方法时,最终会在cc对象上找到这些方法。
‘暧昧’三角关系的奥秘
构造函数、原型对象和实例对象之间的关系可以概括为:
- 构造函数通过
new关键字创建实例对象,this指向实例对象。 - 实例对象通过内部的
[[Prototype]]链接指向原型对象。 - 原型对象可以通过构造函数的
prototype属性进行访问和修改。
图解:
这种“三角关系”使得JavaScript能够在保持简洁的同时,实现强大的面向对象编程能力。每个实例对象都能访问到原型对象上的属性和方法,而原型对象则作为共享资源的中心。
对比于ES6中class的好处:
- 动态修改原型:可以在运行时动态地修改原型对象,从而影响所有已存在的实例对象。例如,可以为原型对象添加新的方法或属性,所有实例对象都会立即获得这些新的特性。
- 自由组合原型:可以将多个原型对象组合起来,形成复杂的继承关系。例如,可以将一个对象的原型设置为另一个对象,实现多继承的效果。
- 直观的原型链:通过查看对象的
__proto__属性,可以清晰地了解对象的继承关系,这对于调试和理解代码非常有帮助。
结语:
希望本文对你有所帮助,写的不好也请多多见谅。