js中有六大类型:string、number、boolean、object、null、undefined。
console.log(typeof "123");//string console.log(typeof 123);//number console.log(typeof true);//boolean console.log(typeof {});//object console.log(typeof null);//object console.log(typeof undefined);//undefined
其中string、number、boolean、null、undefined称为简单基本类型,object为对象类型。js有两种生成对象的方式,一种是字面量(var o = {...}),另一种是构造调用(var o = new Array(...)),通常我们都会使用第一种字面量形式。访问对象属性也有两种方式,一种是通过“.”访问,另一种是通过“[]”访问,通常使用前者访问,但通过后者可以定义/访问动态属性名,功能更加强大(比如obj[a+b]=1)。
最基础的类型为对象类型的对象为Object,Object有很多子对象,比如String、Number、Boolean,Function、Array等,String、Number、Boolean可以看成是基本类型string、number、boolean的包装类型,js引擎在执行时如果有必要会自动将基本类型“打包”成其相应的对象类型。
通常情况下,所有的对象类型都会“继承”Object对象,但也有例外的情况:
var a1 = {}; var a2 = Object.create(null);
查看window的这两个属性,a2便不“继承”Object对象:
String
常用方法:concat,indexOf,lastIndexOf,replace,split,substr,substring等。
Number
常用方法:toFix,toString等。
Boolean
Array
常用方法:concat,join,push,pop,shift,sort
数组在使用“[]”操作属性时,如果"[]"中的内容为字符串型的数字则会被视为数字,从而对数组元素进行操作。而普通对象在使用“[]”操作属性时,则会将“[]”中的数字视为字符串,从而对对象属性进行操作。
var arr = ["a"]; arr.push("b"); console.log(arr[1]);//b console.log(arr["1"]);// console.log(arr.length);//2 var obj = {0: "a"}; obj[1] = "b"; console.log(obj[1]);//b console.log(obj["1"]);//b
Function
Date
RegExp
Error
复制对象内容
var newObj = JSON.parse(JSON.stringify(obj));
ES6提供的浅复制方法Object.assign(target, ......sources)
var newObj = Object.assign(Object.create(null), obj1, obj2);
数据描述符与访问描述符
从ES5开始,我们可以通过Object.getOwnPropertyDescriptor获取对象的属性描述符(描述对象属性的一个数据结构(对象),又分为数据描述符和访问描述符):
var obj = { a: "hello" } console.log(Object.getOwnPropertyDescriptor(obj, "a"));
控制台打印如下,可以看到一个对象的某个属性的数据描述符是一个Object的子对象,且一个数据描述符对象拥有value、writable、enumerable、configurable四个属性。
同样的,我们可以对属性描述符进行设置:
var obj = {}; Object.defineProperty(obj, "a", { value: 2, writable: true, configurable: true, enumerable: true }); obj.a;// 2
我们在访问对象的属性时,看似是直接操作于该属性,实则js引擎会通过调用内部的[[Get]]/[[Put]]逻辑对属性进行操作,这时属性描述符对象就有用了。比如obj.a=3,这时就会走[[Put]]逻辑,首先判断a属性是否存在,如果存在,[[Put]]会判断a属性是否可写,即writable是否为true,如果为true才会更新a的值。
通过数据描述符,我们就可以对对象属性进行访问控制了,比如是否可更新,是否可枚举(通过for in遍历)。ES5进一步提供了一些方法用于控制对象属性的访问策略,比如可以通过Object.preventExtensions(..)、Object.seal(..)、Object.freeze(..)等函数设置对象(及其属性)的不可变性级别。
[[Get]]/[[Put]]是js引擎内部逻辑,我们是否可以重写某些属性的[[Get]]/[[Put]]逻辑呢?ES5中,可以使用getter/setter重写默认逻辑(也可以这样理解,[[GET]]/[[PUT]]首先会尝试getter/setter,如果二者未定义则使用数据描述符,访问描述符和数据描述二者只能定义其一),getter/setter就被称为访问描述符。
1 obj = { 2 a: "hello", 3 get a(){//必须放在a: "hello"之后,否则会被默认逻辑覆盖 4 return "world"; 5 },//默认enumerable:true, configurable:true 6 } 7 console.log(obj.a);//world 8 //or 9 Object.create(null, { 10 { 11 a: { 12 get: function(){return "world";}, 13 set: function(value){this._a_ = value}, 14 enumerable: true, 15 configurable: true,//不能对访问描述符设置writable和value 16 } 17 } 18 });
对象的遍历
我们知道数组可以使用for in,通过数组元素的索引来遍历数组。ES6中数组对象内部提供了一个迭代器(可以通过属性名Symbol.iterator查看,是一个native code),可以通过该迭代器实现for of遍历:
var arr = ["a", "b", "c"]; for(let v of arr){ console.log(v);//通过for of得到的是数组的值,而不需要再通过索引来找值了 }
除了for of,ES5数组还有forEach、every、some等遍历函数。
js对象很像一个map,key就是属性名,value就是属性值,那么普通对象是否可以像数组一样使用for of进行遍历呢?js并没有为普通对象提供类似的迭代器,我们只能使用for in通过数组元素的索引对数组进行遍历。但我们可以自定义一个迭代器,通过该迭代器就可以使用for of了:
1 function generateIterator(){ 2 var obj_ = this; 3 var keys = Object.keys(obj); 4 var i = 0; 5 return { 6 next: function(){ 7 return { 8 value: obj_[keys[i++]], 9 done: i>keys.length 10 } 11 } 12 } 13 } 14 var obj = { 15 a: "hello", 16 b: "world" 17 }; 18 Object.defineProperty(obj, Symbol.iterator 19 , { 20 writable: false, 21 enumerable: false, 22 configurable: true, 23 value: generateIterator 24 }); 25 //手动遍历 26 for(let ite=obj[Symbol.iterator](), next=ite.next(); !next.done; next=ite.next()){ 27 console.log(next.value); 28 } 29 //for of 30 for(var v of obj){ 31 console.log(v); 32 }