在袁老师哪里看到了class降级到es5构造函数的写法,想着记录下来分享给有想要学习的伙伴们,首先看一段class的代码
lass Product{
static count = 0;
constructor(name,unitPrice,number){
this.name = name;
this.unitPrice = unitPrice;
this.number = number;
Product.count++;
}
get totalPrice(){
return this.number * this.unitPrice;
}
increase(){
this.number++;
}
}
相信大家看到之后都很熟悉,一个l类的静态属性count,在constructor()方法中做了初始化了,加了一个取值函数totalPrice和一个increase方法。看到的第一眼我想到的写法是这样的
function Product(name,unitPrice,number){
this.name = name;
this.unitPrice = unitPrice;
this.number = number;
Product.count++;
}
Product.count = 0;
Product.prototype.increase = function(){
this.number++;
}
构造函数初始化,静态变量放到构造函数自身上,方法定义在原型上,诈一眼看好像没啥问题,但是我们需要注意几个问题
- class是和let,const一样是具有作用域死区的;所以不能在类之前调用这个类然而我们上面那样写的构造函数是可以的;
- ES6中调用类只能使用new来调用,然后我们构造函数是可以直接当作普通函数调用的;
- 模拟类中的访问器,另外还需要注意一点es6中的访问器不仅会出现在原型上还会出现在实例上,实例上也不能忘记加入;
- es6中访问器属性和方法是不可枚举的,需要限制;
- es6中是不能使用new来调用类中的方法得,列如let p = new Product(‘辣条’,1,2) console.log(“p—>”, new p.totalPrice)会报错,然而es5可以,所以需要加入判断来限制;
问题一的解决办法
虽然无法在es5中实现暂时性死区但是我们可以使用一个变量来接收,然后将这个函数放到立即执行函数中,返回构造函数本身,代码如下,这样在函数之前调用就会报错了,避免了构造函数可以在之前调用的情况;
var Product = (function(){
function Product(name,unitPrice,number){
this.name = name;
this.unitPrice = unitPrice;
this.number = number;
Product.count++;
}
Product.count = 0;
Product.prototype.increase = function(){
this.number++;
}
return Product;
})();
问题二的解决办法
加一个判断,来判断是否使用new来调用的,如果这个实例的隐式原型等于构造函数的显示原型那么就是使用new来调用的,代码如下
function Product(name,unitPrice,number){
if(Object.getPrototypeOf(this)!== Product.prototype){
throw new TypeError("Class constructor Product cannot be invoked without 'new'")
}
this.name = name;
this.unitPrice = unitPrice;
this.number = number;
Product.count++;
}
问题三的解决办法
解决方法使用object.defineProperty(),代码如下
Object.defineProperty(this,'totalPrice',{
get(){
return this.number * this.unitPrice;
}
})
Object.defineProperty(Product.prototype,'totalPrice',{
get(){
return this.number * this.unitPrice;
}
})
问题四的解决办法,
添加一个不可枚举的属性,设置为false,代码如下
Object.defineProperty(this,'totalPrice',{
get(){
return this.number * this.unitPrice;
},
enumerable:false
})
Object.defineProperty(Product.prototype,'totalPrice',{
get(){
return this.number * this.unitPrice;
},
enumerable:false
})
Object.defineProperty(Product.prototype,'increase',{
enumerable:false,
value:function(){
this.number++;
}
})
问题五的解决办法
在方法中加入判断,代码如下
Object.defineProperty(Product.prototype,'increase',{
enumerable:false,
value:function(){
if(Object.getPrototypeOf(this) === Product.prototype.increase.prototype){
throw new TypeError("Class constructor Product cannot be invoked without 'new'")
}
this.number++;
}
})
完整代码奉上
var Product = (function(){
function Product(name,unitPrice,number){
if(Object.getPrototypeOf(this)!== Product.prototype){
throw new TypeError("Class constructor Product cannot be invoked without 'new'")
}
this.name = name;
this.unitPrice = unitPrice;
this.number = number;
Product.count++;
}
Object.defineProperty(this,'totalPrice',{
get(){
return this.number * this.unitPrice;
},
enumerable:false
})
Object.defineProperty(Product.prototype,'totalPrice',{
get(){
return this.number * this.unitPrice;
},
enumerable:false
})
Product.count = 0;
Object.defineProperty(Product.prototype,'increase',{
enumerable:false,
value:function(){
if(Object.getPrototypeOf(this) === Product.prototype.increase.prototype){
throw new TypeError("Class constructor Product cannot be invoked without 'new'")
}
this.number++;
}
})
return Product;
})();