传统面向对象的语言几大特征:封装、继承、多态,在JavaScript中并不适用。JavaScript的弱类型特征允许你用比其他语言更少的代码完成同样的任务。无需提前设计好类再进行编码。需要一个具有某个字段的对象了?随时随地都可创建。Nicholas
C.Zakas著作《JavaScript面向对象精要》告知我们如何创建对象、定义自己的类型,使用继承以及其他各种操作来充分使用对象。总之,更全面理解和使用JavaScript所有的一切。
一、原始类型和引用类型
1. 什么是类型
JavaScript虽然没有类的概念,但依然存在两种类型:原始类型和引用类型。
原始类型保存为简单数据值;引用类型则保存为对象,其本质是指向内存位置的引用。
var obj = new Object();
变量实际上并不包含对象的实例,而是一个指向内存中实际对象所在位置的指针(或者说引用)。这是对象和原始值之间的一个基本区别。原始值都是直接保存在变量中。
附:关于变量详见【变量、作用域和内存问题】
2. 内建类型实例化
示例:对象字面形式
var obj1 = {};
var obj2 = new Object();
对象字面量,JavaScript引擎背后做的工作挺new Object()
一样,除了没有调用构造函数。
示例:正则表达式字面形式
var reg1 = /^\d+$/g;
var reg2 = new RegExp("^\\d+$", "g");
reg2.toString(); // "/^\d+$/g"
reg2.source; // "^\d+$"
使用字面下个形式创建正则,无需担心字符串中的转义字符。如果使用RegExp构造函数,传入模式的参数是一个字符串,需要转移。
3. 访问属性
示例:对待特殊字符的属性
var obj = {
"ab": "yes",
"a$b": "yes",
"abc": "yes"
};
obj.ab; // "yes"
obj["a$b"]; // "yes"
var name = "abc";
obj[name]; // "yes"
“中括号”可以处理动态决定访问哪个属性以及访问包含特殊字符的属性;其他方面同“点号”大致相同。
4. 鉴别引用类型
鉴别引用类型使用instanceof操作符。
示例:数组
Array.isArray()
[] instanceof Array
需要注意不同框架之间的判断。详见,【JavaScript高级技巧-安全的类型检测】
5. 原始封装类型
示例:自动创建的基本包装类型
var name = "string";
name instanceof String; // false
new String("string") instanceof String; // true
临时对象,仅在值被读取时创建,然后立即被销毁。instanceof操作符并没有真的读取任何东西。
二、函数
函数也是对象。使函数不同于其他对象的特性是其内部具有[[Call]]
属性。该属性无法通过代码访问而是定义了代码执行时的行为。ECMAScript定义typeof操作符对任何具有[[Call]]
属性的对象返回“function”。
示例:判断是否为function
typeof function(){}; // "function"
typeof /\./; // "object"
注意:某些浏览器曾经在正则表达式中包含[[Call]]属性,导致其被错误鉴别为函数。
1. 函数提升
声明本身会被提升,而包括函数表达式的赋值在内的赋值操作并不会提升;后面的函数声明可以覆盖前面的。
示例:函数提升
a(); // "a..."
b(); // Uncaught TypeError: b is not a function(…)
function a(){
console.log("a");
};
function a(){
console.log("a...");
};
var b = function(){
console.log("b");
}
2. 改变this
示例:obj拥有sayName()方法
var obj = {
name: "obj",
sayName: function(age){
console.log("my name is:" + this.name + ",age: " + age);
}
}
(1)call()方法
var obj1 = {name: "call"};
obj.sayName.call(obj1, 25); // my name is:call,age: 25
(2)apply()方法
var obj2 = {name: "apply"};
obj.sayName.apply(obj2, [25]); // my name is:apply,age: 25
(3)bind()方法
var obj3 = {name: "bind"};
var sayName = obj.sayName.bind(obj3);
sayName(25); // my name is:bind,age: 25
注意:call()
、apply()
立即执行,bind()
返回一个函数,并不是立即执行。
关于函数。详见,【JavaScript提升(你不知道的JavaScript)】、【函数表达式】
三、理解对象
1. 定义属性
当一个属性第一次被添加给对象时,JavaScript在对象上调用一个名为[[Put]]的内部方法;当一个已有的属性被赋予一个新值时,调用的是一个名为[[Set]]
的方法。
var person = {};
person.name = "lg"; // 调用[[Put]]
person.name = "ligang"; // 调用[[Set]]
2. 属性探测
if(person.age){
// do something with age
}
当值是一个null、undefined、0、false、NaN或空字符串时评估为假。由于一个对象属性可以包含这些假值,所以上述示例可能导致错误的假定。注意,这个问题在短路&&同样存在。
if(age in person){
// do something with age
}
in
操作符是探测对象中属性是否存在的最好途径,且不会评估属性的值。
3. 属性类型
属性有两种类型:数据属性和访问器属性。
如果只需要保存数据,通常没有什么理由使用访问器属性–直接使用属性本身即可。但当你希望赋值操作会触发一些行为或者读取的值需要通过计算所需的返回值得到时,访问器属性会非常有用。
var person = {
_name: "lg"
};
Object.defineProperty(person, "name", {
get: function(){
console.log("my name is:" + this._name);
return this._name;
},
set: function(name){
this._name = "爱新觉罗" + name;
}
});
// 等价
var person = {
_name: "lg",
get name(){
console.log("my name is:" + this._name);
return this._name;
},
set name(name){
this._name = "爱新觉罗" + name;
}
};
person.name; // my name is:lg
person.name = "ligang";
person.name; // my name is:爱新觉罗ligang
Vue正式通过Object.defineProperty()
对属性进行监听、通知处理。
注意:创建一个同时具有数据特征和访问器特征的属性,会得到一个错误。