JavaScript 的对象构建和面向对象的编程模式。
- 先看一个正常的属性创建有三种方式: 类 class, 对象字面量, 函数构造式
// 示例1:类class
class WidgetA {
constructor() {
this.appName = "天气应用"
}
getName(){
return this.appName;
}
}
var widget1 = new WidgetA();
console.log(widget1.appName); // 返回 “天气应用”
console.log(widget1.getName()); // 返回 “天气应用”
// 示例2:对象字面量
var WidgetB = {
appName : "天气应用",
getName : function (){
return this.appName;
}
}
console.log(WidgetB.appName); // 返回 “天气应用”
console.log(WidgetB.getName()); // 返回 “天气应用”
// 示例3:函数构造式
function WidgetC(){
this.appName = "天气应用";
this.getName = function (){
return "天气应用";
};
}
var widget3 = new WidgetC();
console.log(widget3.appName); // 返回 “天气应用”
console.log(widget3.getName()); // 返回 “天气应用”
- 如何创建私有属性?
- 用 # 符号创建私有属性 根据最新的ES13(262.ecma-international.org/13.0/) 规范,我们可以通过 # 符号,来定义一个私有的属性。
class WidgetD {
#appName;
constructor(){
this.#appName = "天气应用";
}
getName(){
return this.#appName;
}
}
var widget4 = new WidgetD();
console.log(widget4.appName); // 返回 undefined
console.log(widget4.getName()); // 返回 “天气应用”
- 在 # 问世之前,工程师们是怎么实现私有属性的。主要有闭包、WeakMap 和 Symbol 这三种方式 2.1 用闭包和 IIFE 创建私有属性
// 对象字面量
var WidgetE;
(function(){
var appName = "天气应用";
WidgetE = {
getName: function(){
return appName;
}
};
}());
WidgetE.appName; // 返回 undefined
WidgetE.getName(); // 返回 “天气应用”
2.2 通过构造函数的方式,构造私有属性
// 构造函数
function WidgetF() {
var appName = "天气应用";
this.getName = function(){
return appName;
}
}
var widget6 = new WidgetF();
console.log(widget6.appName); // 返回 undefined
console.log(widget6.getName()); // 返回 “天气应用”
上面 例子中还有一个问题,就是我们每次在创建一个新对象的时候,私有属性都会被重新创建一次,这样就会造成重复工作和冗余内存。解决这个问题的办法就是把通用的属性和功能赋值给 prototype,这样通过同一个构建者创建的对象,可以共享这些隐藏的属性
function WidgetG() {
var appName = "天气应用";
this.getName = function(){
return appName;
}
}
//通用属性和功能赋值给 prototype, 防止私有属性重新创建,造成重复工作和内存冗余
WidgetG.prototype = (function(){
var model = "支持安卓";
return {
getModel: function(){
return model;
}
}
}());
var widget7 = new WidgetG();
console.log(widget7.getName()); // 返回 “天气应用”
console.log(widget7.getModel()); // 返回 “支持安卓”
2.3 用 WeakMap 创建私有属性 WeakMap,它的特点是只接受对象作为键名,键名是弱引用,键值可以是任意的。
var WidgetH;
{
let privateProps = new WeakMap();
WidgetH = function(){
privateProps.set(this,{appName : "天气应用"});
}
WidgetH.prototype.getName = function(){
return privateProps.get(this).appName;
}
}
var widget8 = new WidgetH();
console.log(widget8.appName); // 返回 undefined
console.log(widget8.getName()); // 返回 “天气应用”
2.4 用 Symbol 创建私有属性 Symbol 也是在 ES6 引入的一个新的数据类型,我们可以用它给对象的属性的键名命名
var WidgetI;
{
let privateProps = Symbol();
WidgetI = function(){
this[privateProps] = {appName : "天气应用"};
}
WidgetI.prototype.getName = function(){
return this[privateProps].appName;
}
}
var widget9 = new WidgetI();
console.log(widget9.getName()); // 返回 “天气应用”
- 如何创建静态属性? 静态的属性是属于构造函数的属性,而不是构造对象实例的属性
- 创建公开静态属性
class WidgetJ {
static appName = "天气应用";
static getName(){
return this.appName;
}
}
console.log(WidgetJ.appName); // 返回 “天气应用”
console.log(WidgetJ.getName()); // 返回 “天气应用”
var widget10 = new WidgetJ();
//公开静态属性只能作用于 class 本身
console.log(widget10.appName); // 返回 undefined
console.log(widget10.getName()); // 返回 undefined
- 创建私有静态属性
class WidgetM {
// 通过 static 和 # 组合 创建私有静态属性
static #appName = "天气应用";
static staticGetName(){
return WidgetM.#appName;
}
instanceGetName(){
return WidgetM.#appName;
}
}
console.log(WidgetM.staticGetName()); // 返回 “天气应用”
console.log(WidgetM.appName()); // 返回 “天气应用”
var widget13 = new WidgetM();
console.log(widget13.instanceGetName()); // 返回 “天气应用”