【js‘类’实现】【三种实现方式】

Javascript伪类
Java和与其类似的语言中:如果两个类存在继承关系,则子类会自动继承父类的方法和变量,在子类中可以调用父类的方法和变量(私有的除外)。
那么,在js中想实现继承,我们可以模仿‘类’的特性来实现目的,这次我们先看看用js怎么实现‘类’,下次再了解怎么在这些‘类’上实现继承,实现伪类,最简单的,一般是三种形式:
1, 利用构造函数
2, 基于原型
3, 函数化
说是三种形式,本质都是一样的,都是在原型链上实现的。
1>>构造函数

==============================================================

var Mal = function (name) {
this.name = name;
};
// 对构造器的原型进行扩充
Mal.prototype = {
getName: function () {
return this.name;
},
company: function () {
return this.com || ‘qinsilk’;
}
}
// 利用上面的构造器实例化,体现出Mal的特性:
var myMal = new Mal(‘lxy’);
myMal.getName(); // lxy

// 我们现在想定义两个伪类,也想有基础类Mal的特性,该怎么做呢?

// 首先定义构造函数–这里必须重复定义构造函数,比较繁琐,伪类弊端之一
// MalOfExtendsOne对构造函数进行了扩充
var MalOfExtendsOne = function (name) {
this.name = name + name;
this.com = ‘syt’;
};

// MalOfExtendsTwo则是没有自定义
var MalOfExtendsTwo = function (name) {
this.name = name;
};

// 最后替换他们的prototype为一个Mal实例
MalOfExtendsOne.prototype = new Mal();
MalOfExtendsTwo.prototype = new Mal();
// 重写Mal的方法
MalOfExtendsOne.prototype.getName = function () {
return this.name + ’ ’ + this.company();
};
// 实例化后,查看特性:
var malOfExtendsOne = new MalOfExtendsOne(‘lxy’);
var malOfExtendsTwo = new MalOfExtendsTwo(‘xlfd’);

malOfExtendsOne.getName(); // lxylxy syt
malOfExtendsTwo.getName(); // xlfd

/** 那console.log(malOfExtendsOne instanceof *MalOfExtendsTwo)

  • console.log(malOfExtendsTwo instanceof *MalOfExtendsOne)
    
  • console.log(malOfExtendsOne instanceof Mal)
  • console.log(malOfExtendsTwo instanceof Mal)
  • 会输出什么呢?
    */

// console这两个实例对象:

在这里插入图片描述

======================================================
// 优雅的实现方式:
Function.prototype.method = function (name, func) {
if (!this.prototype[name]) {
Function.prototype[name] = func;
}
};

Function.method(‘inherit’, function (Par) {
this.prototype = new Par();
return this;
});

var MalOfExtendsOne = function (name) {
this.name = name + name;
this.com = ‘syt’;
}
.inherit(MalOfExtendsOne)
.method(‘getName’, function () {
return this.name + ’ ’ + this.company();
});

// 实例化
var malOfExtendsOne = new MalOfExtendsOne(‘lxy’)
··························································································

2>>基于原型
// 基于原型
// 函数对象
function Config () {

}
Config.prototype.name = ‘lxy’;
Config.prototype.com = ‘qinsilk’;
Config.prototype.getName = function () {
return this.name;
};

var config = new Config();
console.log(config.name); // lxy
config.name = ‘lxylxy’;
console.log(config.getName()); // lxylxy
// 对象字面量
var userConfig = {
name: ‘lxy’,
getName: function () {
return this.name
},
company: function () {
return this.com || ‘qinsilk’
}
};

var userOne = Object.create(userConfig);
var userTwo = Object.create(userConfig);

userOne.name = ‘xlfd’;
userOne.com = ‘syt’;
userOne.addAge = function (age) {
if (age) {
return age;
}
};
userOne.getName = function () {
return this.name + ’ age: ’ + this.addAge(18);
};

userOne.getName(); // xlfd age: 18
userTwo.getName(); // lxy
// 然后我们console这两个新的对象:
在这里插入图片描述

3>>函数化
可以看到,上面通过伪类和原型来实现继承的方式,所有属性都是公开的,都没有私有的属性,当然了,第二种通过对象字面量的方式也可以通过如下方式实现“私有”:
Object.defineProperty(userConfig, “name”, {
// configurable: true, 表示能否delete后重写定义和修改其‘属性特征(默认true)’
// enumerable: true, 是否可以支持for-in被读取到(默认true)
writable: false, // 表示是否能修改属性的值(默认true)
value: “lxy”
});
userConfig.name = ‘lxylxy’;
console.log(userConfig.name); // lxy
但,这不是理想的状态,麻烦就不说了,浏览器兼容也有问题,而且在严格模式下会报错。也不是说完全没用,肯定是有需要的场景。

为了实现上诉的需求,我们可以按如下的步骤来实现私有属性和实例—函数化模板:
······················································································································

// 首先定义一个新对象,(对象字面量;实例化一个构造函数;调用会返回对象的函数;Object.create)
var userConfig = function (pri) {
// 然后根据自身的需要定义私有变量和实例
var that,
name = pri.name || ‘lxy’,
com = pri.com || ‘qinsilk’,
setName = function (newName) {
name = newName
},
getName = function () {
return name;
},
company = function () {
return com;
};
// 再给that添加访问私有属性的方法
that = {};
that.setName = setName;
that.getName = getName;
that.company = company;
// 最后返回that
return that;
};

var user1 = userConfig({name: ‘xlfd’});
var user2 = userConfig({});
// console:
在这里插入图片描述

====================================================
// 另一种方式:只要注重做好自定义就可以了,无需关注其他。
var user = function (spec) {
var that = userConfig(spec);
spec.com = spec.com || ‘qinsilk’;
that.setCompany = function (newCom) {
spec.com = newCom;
};
that.getName = function () {
return spec.name + spec.name;
};
return that;
};

// 实例化
var userS = user({name: ‘lxh’, com: ‘syt’});
···········································································································
上面都是es5上实现的,es6后,js已经提供了语法糖class:

var Mal = function (name) {
this.name = name;
};
// 对构造器的原型进行扩充
Mal.prototype = {
getName: function () {
return this.name;
},
company: function () {
return this.com || ‘qinsilk’;
}
}
上面的形式,可改写成:
class Mal{
constructor(name) {
this.name = name;
}

getName() {
return this.name;
}
company() {
return this.com || ‘qinsilk’;
}

}
Class Mal的constructor相当于上面的构造函数Mal,this和上面一样也是指向实例对象。
我们console看看
console.log(typeof Mal)
console.log(Mal === Mal.prototype. constructor)

使用和构造函数一样:var mal = new Mal();
刚才上面的,console.log(malOfExtendsOne instanceof Mal)会输出true
那么,console.log(mal instanceof Mal)
会输出什么呢?

实现私有方法:
class Mal {
setName (set) {
set.call(this, name);
}

// …
}

function set(name) {
return this.name = name;
}
总之,在es5项目里建议用对象字面量的方式来实现类。

猜你喜欢

转载自blog.csdn.net/qq_39643614/article/details/83097438