2020-12-15 Javascript定义类class的三种方式
在面向对象编程中,类(class)是对象(object)的模板,定义了同一组实例共有的属性和方法,Javascript中有三种定义类的方法:构造函数法、Object.create()、极简主义法
一、构造函数法
用构造函数模拟“类”,在其内部用this关键字指代实例对象
所谓“构造函数”,其实就是一个普通函数,但是在其内部使用了this变量,对构造函数使用new运算符,生成实例,并且this变量会绑定在实例对象上
function Cat(name,color){
this.name=name;
this.color=color;
}
// 生成实例对象
var cat1 = new Cat("大毛","黄色");
var cat2 = new Cat("二毛","黑色");
alert(cat1.name); // 大毛
alert(cat1.color); // 黄色
上面的cat1和cat2会有一个constructor属性,指向其构造函数
alert(cat1.constructor == Cat); //true
alert(cat2.constructor == Cat); //true
此外,还有instanceof运算符,验证原型对象与实例对象之间的关系
alert(cat1 instanceof Cat); //true
alert(cat2 instanceof Cat); //true
构造函数模式存在的问题就是资源浪费,浪费内存
每个构造函数都有一个prototype属性,指向另外一个对象,这个对象的所有属性和方法,都会被构造函数的实例继承,这意味着可以把那些不变的属性和方法直接定义在prototype对象上
isPrototypeOf()
:这个方法用来判断某个prototype对象和某个实例之间的关系
alert(Cat.prototype.isPrototypeOf(cat1)); //true
hasOwnPrototype()
:这个方法用来判断某一个属性到底是本地属性还是继承自prototype对象的属性
alert(cat1.hasOwnProperty("name")); // true
alert(cat1.hasOwnProperty("type")); // false
in
:此运算符用来判断某个实例是否含有某个属性,不管是不是本地属性,用来遍历某个对象的所有属性
alert("name" in cat1); // true
alert("type" in cat1); // true
二、Object.create()法
为了解决“构造函数”的缺点,EMCAScript提出了一个新的方法Object.create(),用这个方法,类就是一个对象,不是函数
var Cat = {
name: "大毛",
makeSound: function(){ alert("喵喵喵"); }
};
然后直接用Object.create()生成实例,不需要用到new
var cat1 = Object.create(Cat);
console.log(cat1.name); // 大毛
cat1.makeSound(); // 喵喵喵
遇到浏览器不支持的,用以下代码自行部署
if(!Object.create){
Object.create = function(o){
function F(){};
F.prototype = o;
return new F();
}
}
这种方法比“构造函数法”要简单,但是不能实现私有属性和私有方法,实例对象之间也不能共享数据,对“类”的模拟不够全面
三、极简主义法
封装
:这种方法不使用this和prototype,也是一个对象模拟“类”,在这个类里面,定义一个构造函数createNew(),用来生成实例,然后在createNew()里面定义一个实例对象,把这个实例对象作为返回值
var Cat = {
createNew: function(){
var cat = {};
cat.name = "大毛";
cat.makeSound = function(){alert("喵喵喵")};
return cat;
}
};
使用的时候,调用createNew()方法,就可以得到实例对象
var cat1 = Cat.createNew();
cat1.makeSound(); // 喵喵喵
这种方法的好处是容易理解,结构清晰优雅,符合传统的“面向对象编程”的构造,可以很好的实现继承、私有属性和私有方法、数据共享
继承
:让一个类继承另外一个类,实现起来很方面,只要在前者的createNew()方法中,调用后者的createNew()方法即可
先定义一个Animal类
var Animal = {
createNew: function(){
var animal = {};
animal.sleep = function(){alert("睡懒觉")};
return animal;
}
};
然后在Cat的createNew()方法中,调用Animal的createNew()方法
var Cat = {
createNew: function(){
var cat = Animal.createNew();
cat.name = "大毛";
cat.makeSound = function(){alert("喵喵喵")};
return cat;
}
};
这样得到的Cat实例,就会同时继承Cat类和Animal类
var cat1 = Cat.createNew();
cat1.sleep(); // 睡懒觉
cat1.makeSound(); // 喵喵喵
私有属性和私有方法
:在createNew()方法中,只要不是定义在cat对象上的方法和属性都是私有的
var Cat = {
createNew: function(){
var cat = {};
car sound = "喵喵喵";
cat.makeSound = function(){alert(sound)};
return cat;
}
};
上面的内部变量sound,外部是无法读取的,只有通过cat的公有方法makeSound()获取
var cat1 = Cat.createNew();
console.log(cat1.sound); // undefined
cat1.makeSound(); // 喵喵咪
数据共享
:有时候,我们需要所有实例对象,能够读写同一项内部数据。这个时候,只要把这个内部数据,封装在类对象里面、createNew()方法外即可
var Cat = {
sound: "共享数据",
createNew: function(){
var cat = {};
cat.makeSound = function(){alert(Cat.sound)};
cat.changeSound = function(x){Cat.sound = x;};
return cat;
}
};
然后,生成两个实例对象
var cat1 = Cat.createNew();
var cat2 = cat.createNew();
cat1.makeSound(); // 共享数据
这时候如果有一个实例对象,修改了共享的数据,另一个实例对象也会受影响
cat2.changeSound("喵喵喵");
cat1.makeSound(); // 喵喵喵