ES6(ECMAScript 6.0)之前js没有引入类的概念
在ES6之前,对象不是基于类创建的,而是用一种称为构建函数的特殊函数来定义对象和它们的特征
ES6之前创建对象可以通过以下三种方式创建对象:
对象字面量:
var obj ={
}
new Object():
var obj=new Object()
自定义构造函数:
function Star(name,age){
this.name=name;
this.age=age;
this.sing=function(){
console.log("我会唱歌!")
}
}
var star=new Star("张三",20)
console.log(star.name);
star.sing() //我会唱歌!
构造函数是一种特殊的函数,主要用于初始化对象,他总是与new一起使用,我们把对象的一些公共的属性和方法抽取出来,然后封装到这个函数里面
new 在执行时会做四件事情:
-
在内存中创建一个新的对象
-
让this指向这个新的对象
-
执行构造函数中的代码,给这个新对象添加属性和方法
-
返回这个新的对象
成员即是构造函数包含的属性和函数,而成员又分为:实例成员和静态成员
实例成员
实例成员就是在构造函数内部通过this添加的成员如上面的name、age和sing,实例成员只能通过实例化对象进行访问
var star=new Star("张三",20)
//star.kk=1029 这也是实例成员
console.log(star.name);
静态成员
在构造函数本身上添加的成员
star.sex='男'
console.log(star.sex) //男
静态成员只能通过构造函数进行访问
我们希望所有对象使用同一个函数,这样就比较节省内存,而解决方法就是我们接下来要讲的原型对象prototype(构造函数通过原型分配的函数是所有对象所共享的)
JavaScript规定,每一个构造函数都有一个prototype属性,指向另一个对象。注意这个prototype就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有,下面我们看看打印的构造函数:
我们可以把哪些不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法
以上面代码为例:
Star.prototype.sing=function(){
console.log("我会唱歌")
}
let star=new Star()
star.sing()
所以一般情况下,我们的公共属性定义到构造函数里面,公共的方法放在原型对象上
每一个对象都有一个属性proto指向我们构造函数的原型对象,但是我们不能通过proto对原型对象中的属性进行赋值
接下来我们来仔细探讨一下构造函数、实例和原型对象之间的关系
function Star(username,age){
this.username=username;
this.age=age
}
Star.prototype.sing=function(){
console.log("我会唱歌");
}
let star=new Star()
console.log(Star.prototype); //输出构造函数上的原型对象
console.log(star); //输出对象
上述程序运行结果如下:
对象原型([[Protootype]])和构造函数(prototype)原型对象里面都有一个属性constructor属性,constructor我们称为构造函数,因为它指回构造函数本身
constructor主要用于记录该对象引用与哪个构造函数,它可以让原型对象指向原来的构造函数
很多情况下,我们需要手动的利用constructor这个属性指向原来的构造函数
Star.prototype = {
movie: function(){
console.log("我会演电影');
}
}//prototype是对象,如果这样赋值就会覆盖原来的对象,这会使得原型上没有了构造函数
console.log(Star.prototype)
原型对象上没有了构造函数具体影响取决于你在代码中的使用方式。以下是可能的影响:
-
丢失原型链:通过将
star.prototype
设置为新的对象字面量,你将丢失默认的原型链。默认情况下,原型链是由构造函数和其原型对象构成的。如果没有了默认的原型对象,那么对象实例将不再具有原型链上的属性和方法的继承能力。 -
构造属性丢失:由于新的原型对象没有
constructor
属性,所以对象实例将不再具有指向构造函数的constructor
属性。当你通过检查对象的构造属性来确定其构造函数时,将无法准确地识别对象的类型。 -
对象实例继承变更:新创建的对象实例将继承新原型对象上的属性和方法。在你的示例中,新对象实例将继承
movie
方法。这意味着你可以在新对象上调用movie()
方法,输出 "我会演电影"。
请注意,由于丢失了默认的原型对象和构造属性,这种设置可能会导致代码的可读性和可维护性下降。通常情况下,最好不要完全替换原型对象,而是通过扩展或修改原型对象来添加或覆盖属性和方法。
以下是一个示例,展示了将 star.prototype
设置为新对象字面量的影响:
function Star(name) {
this.name = name;
}
// 将 star.prototype 设置为新对象字面量
Star.prototype = {
movie: function() {
console.log("我会演电影");
}
};
var star1 = new Star("张三");
// 对象实例继承了新原型对象上的方法
star1.movie(); // 输出:我会演电影
// 丢失了默认的构造属性
console.log(star1.constructor === Star); // false
console.log(star1.constructor); // 输出:[Function: Object]
在上述示例中,star1
对象实例继承了新的原型对象上的 movie
方法,可以调用该方法。但它失去了默认的构造属性,constructor
不再指向 Star
构造函数,而是指向 Object
构造函数。
这时我们需要手动添加constructor指回原来的构造函数
通过原型对象扩展内置对象