一、概述
JS是一种面向对象的语言。除了基本数据类型number, string, boolean(true, false), null, undefined,其他的都是对象。对象就是一个"name-value"对集合。
二、操作对象
2.1 创建对象
JS有三种创建对象的方式:字面量,Object.create,new构造方式。
2.1.1 Object.create(null | object)
严格来说调用Object.create方法是创建JS对象的唯一方式(其他两种方式内部实现都是基于该方法的)。该方法功能是创建一个对象,并且该对象的原型指向create的参数对象。
参数:必须是null或者对象,否则报错。null表示创建一个没有原型的空对象。
var p = { a: 1}; // 对象字面量
var c1 = Object.create(p); // 对象c1的原型指向p
var c2 = Object.create(null);// 对象c2没有原型
2.1.2 对象字面量
对象字面量是一种创建对象的便捷方式,见上例。其中对象p的创建方式就是对象字面量。JS解释器会对它进行处理的,等价于:
var p = Object.create(Object.prototype);
p.a = 1;
所以说对象字面量内部也是通过Object.create方式创建对象的,并且所有对象字面量方式创建的对象的原型都执行Object.prototype(如上图)。
思考:JS解释器如何区分语句块花括号{}
和空对象花括号{}
的?
先看看这两条语句执行的结果?
{} + [] // 0
[] + {} // "[object Object]"
-
当
{}
作为右值(赋值表达式右侧),实参,在运算符的右侧,被括号()
包裹则作为对象,其他则视为语句块:
下面输出都是:"[object Object]"console.log({} + []) // 作为实参了 ({}) + [] // 被括号包裹 var a = {} + [] //作为右值 console.log(a)
-
我们知道当ES6箭头函数的函数体只有一条语句时可以省略语句块花括号,但是如果箭头函数返回的是一个对象该如何书写呢?
var func1 = () => {age: 12} // 本意是想返回对象{age:12},显然这样写就不对了,花括号会被作为语句块解析 var func2 = () => ({age: 12}) // 可以用括号包裹下 var func3 = () => { return {age: 12}} // 或显示的写全
2.1.3 new构造方式
JS的作者为了讨好类语言的开发者,引入了第三者创建对象方式,即new构造方式。这使得JS对象的创建有点像类语言的对象创建。
1) 格式
new关键字 + 空格 + 函数名字 + [(参数)]
其中参数是可选的,当没有参数传递时,可以省略括号。如:
function Func(){}
var c1 = new Func();
var c2 = new Func; // 如果没有参数传递,可以省略括号。
function Func(name){
this.name = name;
}
Func.prototype.say = function(){
};
var c = new Func('q');
2) 内部原理
这种方式的内部也是通过Object.create方式构建的。new方式创建对象大致分为三步:
Step1:创建一个对象A,并且对象A的原型指向构造函数的prototype属性
Step2:以对象A绑定到构造函数上调用构造函数
Step3:如果构造函数返回值不是个非null的对象,则返回构造函数的返回值作为new表达式的值,否则以对象A作为new表达式的值。
function Func(name){
this.name = name;
}
Func.prototype.say = function(){
};
function create(){ // 模拟new操作符
var func = arguments[0];
var args = Array.prototype.slice.call(arguments, 1);
var other = Object.create(func.prototype); // Step 1
var result = func.apply(other, args); // Step 2
return typeof result === 'object' && result ? result: other; // Step3 注意返回值
}
var c = create(Func, 'q');
2.2 访问对象属性
访问方式也就是get/set/delete。在get访问中会涉及原型链,set/delete访问不会。
var Obj = {
name: 'john'
};
// Get操作
var n = Obj.name; // 等价var n = Obj["name"];
// Set操作
Obj.age = 12;
2.2.1 Get操作流程:
2.2.2 Set操作流程:
2.2.3 delete操作
可以通过delete操作符删除对象的属性,只能删除对象本身的属性。
var p = {
age: 26
}
var obj = Object.create(p);
obj.name = 'john';
console.log(obj.name); // john
console.log(obj.age); // 26
delete obj.name; // 删除属性
delete obj.age; // 删除属性
console.log(obj.name); // undefined
console.log(obj.age); // undefined
2.3 引用对象
JS中对象是引用类型的。对象在作为值时,是作为引用传递的。
var a={}, b={}; // a,b分别指向不同的对象
var c = d = {}; // c,d指向同一个对象
三、反射
确定对象的类型有时很有必要。
3.1 typeof 操作符
通过typeof操作符可以获取值的类型:
console.log(typeof 1); // number
console.log(typeof ''); // string
console.log(typeof null); // object
console.log(typeof undefined); // undefined
console.log(typeof true); // boolean
console.log(typeof {}); // object
console.log(typeof []); // object
console.log(typeof function(){}); // function
但是发现这种方式中null, 数组也都是返回“object”。原因是JS中没有原生数组类型,数组是通过对象模拟的,所以数组也是对象。但是如何区分数组和对象呢???
3.2 Object.prototype.toString
typeof是有缺陷的,在实际应用中常通过Object.prototype.toString方法确定对象类型的。
console.log(Object.prototype.toString.call(1)); // [object Number]
console.log(Object.prototype.toString.call('')); // [object String]
console.log(Object.prototype.toString.call(null)); // [object Null]
console.log(Object.prototype.toString.call(undefined)); // [object Undefined]
console.log(Object.prototype.toString.call(true)); // [object Boolean]
console.log(Object.prototype.toString.call( {})); // [object Object]
console.log(Object.prototype.toString.call([])); // [object Array]
console.log(Object.prototype.toString.call(function(){})); // [object Function]
看例子中输出结果中发现不同之处了吧。假如判断对象是否为数组:
var isArray = function(val){
return Object.prototype.toString.call(val) === '[object Array]';
}
目前的很多库zeptojs,underscorejs中都是这样实现的。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。