JS中的所有事物都是对象,包括但不限于字符串、数值、数组、函数等等,还包括自定义对象。
在红宝书中,将JS分为五种基本类型:null、undefined、number、string、boolean和一种复杂类型:object。但是在《JavaScript语言精髓与编程实践》认为是6种:undefined、number、string、boolean、object和function,它的依据是typeof的结果只有6种(仅含ES自身,不包括宿主对象),其中null的类型也是object.
ES6新增了一种 Symbol 类型,它是一种不可变的类型,表示独一无二的值。一般作为对象属性的标识符使用。ES6 之前的属性名都是字符串类型,会造成属性名的重写覆盖。ES6 提出 Symbol 类型后,JS的基本类型即增加到了 7 种。
var
s1 = Symbol();
typeof
s1;
// "symbol"
var
s2 = Symbol(
'andy'
);
s2;
// Symbol(andy)
var
a = 1, b =
'2'
, c =
true
, d, e =
null
, f =
function
(){}
typeof
a ===
'number'
;
// true
typeof
b ===
'string'
;
// true
typeof
c ===
'boolean'
;
// true
typeof
d ===
'undefined'
;
// true
typeof
e ===
'object'
;
// true
typeof
f ===
'function'
;
// true
下面是七种基本类型的基本使用场景和方法:(引用链接: http://blog.csdn.net/baidu_32262373/article/details/52615158)
Undefiend 类型
1. Undefined 类型只有一个值,即: undefined 。
2.当用 var 声明一个变量但未初始化值时,此时 js 默认会给这个变量赋值,这个值是 undefined 。
var message; // 此时只声明了变量,未初始化,默认值为 undefined
alert(message); // 返回结果 undefined
3.为什么引入这个数据类型
首先我们得弄清一个概念 —— 去考试了但是考了0分 / 没有去考场,考卷上没有分数。
这个数据类型的创建就是为了解决这个问题。
我们声明了一个变量,只是没有给它赋值而已,它是一个实实在在的存在的东西。
和我们直接使用一个凭空出现的,尚未声明的不存在的变量是两个不同的概念。
var message; // 声明了变量 message
alert(message); // 返回结果 undefined,这个变量已经声明过了,只是没有初始化值
alert(apple); // 返回结果 控制台直接报错。
//(打开F12,选择 控制台或者Console 选项卡会看见红色报错信息)
4.总结
Null 类型
1.Null 类型只有一个值,即: null 。
null 是空对象指针,对象里没有内容,即: null={ }; (注:js 中一对华括弧就是一个对象)
2.Null 和 Undefined 有什么关系?
Undefined 可以看成是 Null 的一个子集,下面的例子可以证实这一点:(了解有这回事,没多大卵用,知道 Null 和 Undefined 是两回事即可)
alert(null == undefined); // 返回结果 true
虽然 Undefined 和 Null 有关系而且很相似,为什么又要同时引入这两个概念呢?看下面。
3.Null 的用处?
如果一个变量被声明出来并且明确了在接下来的操作中会接收一个对象,那么就该显式的为该变量赋值 null 。
在下面的操作中只需要判断这个变量是否等于 null 就可以知道这个变量到底有没有储存一个对象。
var obj = null; // 定义了变量 obj ,并且已经明确这个变量下面就是用来接收对象
// ... obj 变量接收一个对象(对象即:键值对)
obj = {key1: 'value1', key2: 'value2'};
if (obj != null) {
// 判断 obj 如果不为 null,就说明此时已经储存了对象
}
4.总结
简单的说:
Boolean 类型
1.Boolean 类型有两个值: true 和 false 。
2.常见误区:
3.类型转换
除 Boolean 类型外还有五种数据类型,都可以转换为 Boolean 类型。有两种方式:
1)使用 Boolean() 方法
var message = 'This is information';
var asBoolean = Boolean(message);alert(asBoolean); // 返回结果 true
var info = 'i am string';
if (info) {
// 这里的if(info)已经默认调用了Boolean()转换为了true,所以才会执行下面的 alert语句
alert(1); // 返回结果 1
}
4.其它四种数据类型转化为Boolean类型(不包括Null类型)
数据类型 | 转换为true的值 | 转换为false的值 |
---|---|---|
Boolean | true | false |
String | 任何非空字符串 | ”(空字符串) |
Number | 任何非零数字值(包括无穷大) | 0和NaN |
Undefined | 不适用 | undefined |
Object | 任何对象 | null |
Number 类型
1.进制数
十进制:js中默认的进制数
八进制:第一位必须是零,然后是0-7的数字组成
十六进制:前两位必须是0x,然后是0-9及A-F(字母不区分大小写)
例:
var num1 = 10; // 十进制
var num2 = 070; // 八进制的56
var num3 = 079; // 十进制,因为有数字超过了7,这里是79
var num4 = 0x1f; // 十六进制的31
注:
八进制在严格模式下("use strict")是无效的,会导致js报错,避免使用。
2.浮点值(即小数)
看个例子:
var num = 0.1 + 0.2;
alert(num); // 返回结果:0.30000000000000000004
上面例子表达的就是js的浮点型数据在计算时容易丢失精度,这一点并不仅在javascript存在,建议处理这方面问题使用专用的数字处理类,比如Java里的BigDecima类来处理。这里略过。
3.数字的范围
js中数值的范围是有效位数的,基本上够我们使用,我们仅需要知道以下几个知识点:
使用 isFinite() 方法确认一个数值是否在js有效数值返回内
如:
var num = Number.MAX_VALUE + Number.MAX_VALUE;
alert(isFinite(num)); // 返回 false,无穷大加无穷大超出js数值范围
3.NaN (not a number)
问:为什么要引入这个东西?
答:在其他语言中,比如java中 1/0 由于0作为分母就会报错,但在js中就不会,直接返回NaN。
NaN的含义是本该返回数值的操作未返回数值,返回了NaN就不会抛出异常影响语句流畅性。
掌握:isNaN()的使用?判断一个值是不是数值,不是数值返回true,是数值返回false
如:
alert(isNaN(NaN)); // true 不是数值
alert(isNan(10)); // false 是数值
alert(isNan(false)); // false 自动转换为数值 0
alert(isNan('10')); // false 自动转换为数值 10
alert(isNan(hahah)); // true 不是数值
在上面的例子中,会发现有些值被自动的转换为了数值。这就和Boolean类型中的分支条件语句也会将所有数据类型自动转换Boolean一样。
if('hello'){ // 这里的'hello'被转换为了Boolean类型的true值
alert('i am print');
}
4.类型转换
就像Boolean类型有个Boolean()方法可以在某些情形下自动将其它数据类型转换为Boolean类型;
Number也提供了Number()方法在某些情形将其它数据类型转换为Number类型。
数据类型 | 返回结果 | 情形 |
---|---|---|
null | 返回0 | - |
undefined | 返回0 | - |
Boolean | true返回1,false返回0 | - |
String | ‘123’返回 123,’011’转换为 11 | 字符串中只包含数字,省略前导0 |
‘1.1’返回 1.1,’02.2’返回 2.2 | 字符串只包含小数,省略前导0 | |
‘0xf’返回 15 | 转换为十进制再返回 | |
”返回 0 | 空字符串转换为0 | |
- | 除字符串的其它情形返回NaN | |
Object | 待定 | 调用valueOf()方法再参照上述规则 |
parseInt()方法介绍:只能将字符串转换为数值型
如:
var num1 = Number('123abc'); // NaN
var num2 = Number('123abc'); // 123
问:为什么有Number()了还需要parseInt()方法?
答:比如上面的例子中使用Number()方法就不能返回数值,因为它要求字符串必须全部数值才行,而parseInt的解析字符串机制让它在某些情况下优于Number。
parseInt()处理机制:
首先解析字符串的第一个字符,如果不是数值直接返回,如果是数字,再接着往下解析,直到遇到不是数字的字符时停止,返回前面解析过的值。
使用parseInt()的好习惯是加上进制数。第二个参数定义转换的进制数。
如:
var num1 = parseInt('AF', 16); // 175
var num2 = parseInt('AF'); // NaN
上面例子中num1会返回数值,因为定义为了16进制数,而num2只是被当做普通字符串解析。
String 类型
1.类型转换
toString() 方法
对数值使用toString()时,可以加进制数限制结果
如:
var num = 10;
alert(num.toString(2)); // '1010'
alert(num.toString(8)); // '12'
String() 方法
由于toString()方法不能对null和undefined进行转换,那么当我们不确定这个值是不是null或undefined时如何转换呢?String()可以对任何值进行转换。
如:
var aa = null;
var bb;
String(aa); // 'null'
String(bb); // 'undefined'
Object 类型(引用链接:http://luopq.com/2016/02/28/Object-in-Javascript/)
Object是在javascript中一个被我们经常使用的类型,而且JS中的所有对象都是继承自Object对象的。虽说我们平时只是简单地使用了Object对象来存储数据,并没有使用到太多其他功能,但是Object对象其实包含了很多很有用的属性和方法,尤其是ES5增加的方法,因此,本文将从最基本的介绍开始,详细说明了Object的常用方法和应用。
基础介绍
创建对象
首先我们都知道,对象就是一组相似数据和功能的集合,我们就是用它来模拟我们现实世界中的对象的。那在Javascript中,创建对象的方式通常有两种方式:构造函数和对象字面量。
new构造函数法
1
2
3
|
var
person =
new
Object();
person.name =
"狼狼的蓝胖子"
;
person.age = 25;
|
这种方式使用new关键字,接着跟上Object构造函数,再来给对象实例动态添加上不同的属性。这种方式相对来说比较繁琐,一般推荐使用对象字面量来创建对象。
对象字面量
对象字面量很好理解,使用key/value的形式直接创建对象,简洁方便。
1
2
3
4
|
var
person = {
name: “狼狼的蓝胖子”,
age: 25
};
|
这种方式直接通过花括号将对象的属性包起来,使用key/value的方式创建对象属性,每个属性之间用逗号隔开。
注意:如果是最后一个属性,后面就不要加逗号,因为在一些旧的浏览器下会报错。
对象实例的属性和方法
不管通过哪种方式创建了对象实例后,该实例都会拥有下面的属性和方法,下面将会一一说明。
constructor属性
constructor属性是保存当前对象的构造函数,前面的例子中,constructor保存的就是Object方法。
1
2
3
4
5
6
7
8
|
var
obj1 =
new
Object();
obj1.id =
"obj1"
;
var
obj2 = {
"id"
:
"obj2"
};
console.log(obj1.constructor);
//function Object(){}
console.log(obj2.constructor);
//function Object(){}
|
hasOwnProperty(propertyName)方法
hasOwnProperty方法接收一个字符串参数,该参数表示属性名称,用来判断该属性是否在当前对象实例中,而不是在对象的原型链中。我们来看看下面这个例子:
1
2
3
|
var
arr = [];
console.log(arr.hasOwnProperty(
"length"
));
//true
console.log(arr.hasOwnProperty(
"hasOwnProperty"
));
//false
|
在这个例子中,首先通过定义了一个数组对象的实例arr,我们知道数组对象实际是通过原型链继承了Object对象,然后拥有自己的一些属性,我们通过hasOwnProperty方法判断length是arr自己的属性,而hasOwnProperty是在原型链上的属性。
hasOwnProperty方法可以和for..in结合起来获取对象自己的key。
isPrototypeOf(Object)方法
isPrototype方法接收一个对象,用来判断当前对象是否在传入的参数对象的原型链上,说起来有点抽象,我们来看看代码。
1
2
3
|
function
MyObject() {}
var
obj =
new
MyObject();
console.log(Object.prototype.isPrototypeOf(obj));
|
我们知道MyObject是继承自Object对象的,而在JS中,继承是通过prototype来实现的,所以Object的prototype必定在MyObject对象实例的原型链上。
propertyIsEnumerable(prototypeName)方法
prototypeIsEnumerable用来判断给定的属性是否可以被for..in语句给枚举出来。看下面代码:
1
2
3
4
5
6
|
var
obj = {
name:
"objName"
}
for
(
var
i
in
obj) {
console.log(i);
}
|
执行这段代码输出字符串“name”,这就说明通过for…in语句可以得到obj的name这个属性,但是我们知道,obj的属性还有很多,比如constructor,比如hasOwnPrototype等等,但是它们没有被输出,说明这些属性不能被for…in给枚举出来,可以通过propertyIsEnumerable方法来得到。
判断“constructor”是否可以被枚举,输出false说明无法被枚举出来。
toLocaleString()方法
toLocalString方法返回对象的字符串表示,和代码的执行环境有关。
1
2
3
4
5
|
var
obj = {};
console.log(obj.toLocaleString());
//[object Object]
var
date =
new
Date();
console.log(date.toLocaleString());
//2016/2/28 下午1:39:27
|
toString()方法
toString用来返回对象的字符串表示。
1
2
3
4
5
|
var
obj = {};
console.log(obj.toString());
//[object Object]
var
date =
new
Date();
console.log(date.toString());
//Sun Feb 28 2016 13:40:36 GMT+0800 (中国标准时间)
|
valueOf()方法
valueOf方法返回对象的原始值,可能是字符串、数值或bool值等,看具体的对象。
1
2
3
4
5
6
7
8
9
10
|
var
obj = {
name:
"obj"
};
console.log(obj.valueOf());
//Object {name: "obj"}
var
arr = [1];
console.log(arr.valueOf());
//[1]
var
date =
new
Date();
console.log(date.valueOf());
//1456638436303
|
如代码所示,三个不同的对象实例调用valueOf返回不同的数据。
属性的类型
在Javascript中,属性有两种类型,分别是数据属性和访问器属性,我们来看看这两种属性具体是什么东西。
数据属性
数据属性我们可以理解为我们平时定义对象时赋予的属性,它可以进行读和写。但是,ES5中定义了一些特性,这些特性是用来描述属性的各种特征,特性是内部值,不能直接访问到。特性通过用两对方括号表示,比如[[Enumerable]]。属性的特性会有一些默认值,要修改特性的默认值,必须使用ES5定义的新方法Object.defineProperty方法来修改。
数据属性有4个描述其特征的特性,下面将依次说明每一个特性:
(1)[[Configurable]]:该特性表示是否可以通过delete操作符来删除属性,默认值是true。
1
2
3
4
5
|
var
obj = {};
obj.name =
"myname"
;
delete
obj.name;
console.log(obj.name);
//undefined
|
这段代码很明显,通过delete删除了obj的name属性后,我们再访问name属性就访问不到了。
我们通过Object.defineProperty方法来修改[[Configurable]]特性。
1
2
3
4
5
6
7
8
|
var
obj = {};
obj.name =
"myname"
;
Object.defineProperty(obj,
"name"
, {
configurable:
false
})
delete
obj.name;
console.log(obj.name);
//myname
|
通过将configurable特性设置成false之后,delete就无法删除name属性了,如果在严格模式下,使用delete去删除就会报错。
(2)[[Enumerable]]:表示是否能够通过for…in语句来枚举出属性,默认是true
我们来看看前面的例子:
1
2
3
4
5
6
|
var
obj = {
name:
"objName"
}
for
(
var
i
in
obj) {
console.log(i);
//name
}
|
这段代码只输出了name属性,我们来将constructor属性的[[Enumerable]]设置为true试试。
1
2
3
4
5
6
7
8
9
10
11
|
var
obj = {
name:
"objName"
}
Object.defineProperty(obj,
"constructor"
, {
enumerable:
true
})
for
(
var
i
in
obj) {
console.log(i);
//name,constructor
}
console.log(obj.propertyIsEnumerable(
"constructor"
));
//true
|
这段代码中,for…in循环得到了name和constructor两个属性,而通过propertyIsEnumerable方法来判断constructor也返回了true。
(3)[[Writable]]:表示属性值是否可以修改,默认为true
如果[[Writable]]被设置成false,尝试修改时将没有效果,在严格模式下会报错
(4)[[Value]]:表示属性的值,默认为undefined
我们通过一个简单的例子来看看这两个特性:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
var
obj = {
name:
"name"
};
console.log(obj.name);
//name
Object.defineProperty(obj,
"name"
, {
value:
"newValue"
,
writable:
false
})
console.log(obj.name);
//newValue
obj.name =
"oldValue"
;
console.log(obj.name);
//newValue
|
我们首先定义了obj对象的name属性值为“name”,然后通过defineProperty方法来修改值,并且将其设置为不可修改的。接着我们再修改name属性的值,可以发现修改无效。
如果我们通过defineProperty来修改name属性的值,是否可以修改呢?答案是可以的:
1
2
3
4
|
Object.defineProperty(obj,
"name"
, {
value:
"oldValue"
})
console.log(obj.name);
//oldValue
|
访问器属性
访问器属性有点类似于C#中的属性,和数据属性的区别在于,它没有数据属性的[[Writable]]和[[Value]]两个特性,而是拥有一对getter和setter函数。
[[Get]]:读取属性时调用的函数,默认是undefined
[[Set]]:设置属性时调用的函数,默认是undefined
getter和setter是一个很有用的东西,假设有两个属性,其中第二个属性值会随着第一个属性值的变化而变化。这种场景在我们平时的编码中起始是非常常见的。在之前的做法中,我们往往要去手动修改第二个属性的值,那现在我们就可以通过get和set函数来解决这个问题。看下面这个例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
var
person = {
age: 10
}
Object.defineProperty(person,
"type"
, {
get:
function
() {
if
(person.age > 17) {
return
"成人"
;
}
return
"小孩"
;
}
})
console.log(person.type);
//小孩
person.age = 18;
console.log(person.type);
//成人
|
通过修改age的值,type的值也会相应的修改,这样我们就不用再手动的去修改type的值了。
下面这种方式也是可以实现同样的效果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
var
person = {
_age: 10,
type:
"小孩"
}
Object.defineProperty(person,
"age"
, {
get:
function
() {
return
this
._age;
},
set:
function
(newValue) {
this
._age = newValue;
this
.type = newValue > 17 ?
"成人"
:
"小孩"
;
}
})
console.log(person.type);
person.age = 18;
console.log(person.type);
|
关于访问器属性,有几点要注意:
1、严格模式下,必须同时设置get和set
2、非严格模式下,可以只设置其中一个,如果只设置get,则属性是只读的,如果只设置set,属性则无法读取
3、Object.defineProperty是ES5中的新方法,IE9(IE8部分实现,只有dom对象才支持)以下浏览器不支持,一些旧的浏览器可以通过非标准方法defineGetter()和defineSetter()来设置,这里就不说明了,有兴趣的同学可以查找相关资料。
特性操作的相关方法
ES5提供了一些读取或操作属性特性的方法,前面用到的Object.defineProperty就是其中之一。我总结了一些比较常用的方法如下:
(1)Object.defineProperty
定义一个对象的属性,这个方法前面我们已经用到多次,简单说说其用法。
defineProperty有点类似于定于在Object上的静态方法,通过Object直接调用,它接收3个参数:
obj:需要定义属性的对象
propNane:需要被定义的属性名称
defineProperty:属性描述符,包含一些属性的特性定义
例子如下:
1
2
3
4
5
6
7
|
var
obj = {};
Object.defineProperty(obj,
"name"
, {
value:
"name"
,
configurable:
true
,
writable:
true
,
enumerable:
true
});
|
(2)Object.defineProperties
和defineProperty类似,是用来定义对象属性的,不同的是它可以用来同时定义多个属性,我们通过命名也可以看出来,用法如下:
1
2
3
4
5
6
7
8
9
10
11
12
|
var
obj = {};
Object.defineProperty(obj, {
"name"
: {
value:
"name"
,
configurable:
true
,
writable:
true
,
enumerable:
true
},
"age"
: {
value: 20
}
});
|
(3)Object.getOwnPropertyDescriptor
ES5中还提供了一个读取特性值的方法,该方法接收对象及其属性名作为两个参数,返回一个对象,根据属性类型的不同,返回对象会包含不同的值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
var
person = {
_age: 10,
type:
"小孩"
}
Object.defineProperty(person,
"age"
, {
get:
function
() {
return
this
._age;
},
set:
function
(newValue) {
this
._age = newValue;
this
.type = newValue > 17 ?
"成人"
:
"小孩"
;
}
})
console.log(Object.getOwnPropertyDescriptor(person,
"type"
));
//Object {value: "成人", writable: true, enumerable: true, configurable: true}
console.log(Object.getOwnPropertyDescriptor(person,
"age"
));
//Object {enumerable: false, configurable: false, get: function(),set: function ()}
|
Object的方法
在ES5中,Object对象上新增了一批方法,这些方法可以直接通过Object进行访问,前面用到的defineProperty就是新增的方法之一。除此之外还有很多方法,我将其总结归纳如下:
对象创建型方法
Object.create(proto, [propertiesObject])
在前面我们提到,创建一个对象有两种方法:构造函数和对象字面量。
这两种方法有一个缺点就是:如果要创建多个对象,写起来很繁琐,所以后来就有了一种创建自定义构造函数的方法来创建对象,如下所示:
1
2
3
4
5
6
|
function
Person(name, age) {
this
.name = name;
this
.age = age;
}
var
person =
new
Person(
"Jack"
, 15);
|
这种方式可以很方便的创建多个同样的对象,也是目前比较常用的方法。
ES5提供的Object.create方法也是一个创建对象的方法,这个方法允许为创建的对象选择原型对象,不需要定义一个构造函数。用法如下:
1
2
3
4
5
6
|
var
obj = Object.create(Object.prototype, {
name: {
value:
"Jack"
}
})
console.log(obj.name);
//Jack
|
这个方法接收的第一个参数作为被创建对象的原型,第二个参数是对象的属性。注意:在这个例子中,name属性是无法被修改的,因为它没有设置writable特性,默认则为false。
个人看法:Object.create这种创建对象的方式略显繁琐,除非是需要修改属性的特性,否则不建议使用这种方式创建对象。
属性获取型方法
Object.keys
Object.keys是用来获取给定对象的所有可枚举的自身属性的属性名,它返回一个数组。
1
2
3
4
5
6
7
8
9
10
|
function
Parent() {
this
.lastName =
"Black"
}
function
Child(firstName) {
this
.firstName = firstName;
}
Child.prototype =
new
Parent();
var
son =
new
Child(
"Jack"
);
console.log(Object.keys(son));
//["firstName"]
|
代码中返回了firstName,并没有返回从prototype继承而来的lastName和不可枚举的相关属性。
在一些旧的浏览器中,我们可以使用hasOwnProperty和for…in来达到类似的效果。
1
2
3
4
5
6
7
8
9
10
11
|
Object.keys = Object.keys ||
function
(obj) {
var
keys = [];
for
(
var
key
in
obj) {
if
(obj.hasOwnProperty(key)) {
keys.push(key);
}
}
return
keys;
}
Object.getOwnPropertyNames()
|
getOwnPropertyNames用来获取对象自身的所有属性,包括可枚举和不可枚举的所有属性,如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
function
Parent() {
this
.lastName =
"Black"
}
function
Child(firstName) {
this
.firstName = firstName;
}
Child.prototype =
new
Parent();
var
son =
new
Child(
"Jack"
);
Object.defineProperty(son,
"age"
, {
enumerable:
false
})
console.log(Object.keys(son));
//["firstName"]
console.log(Object.getOwnPropertyNames(son));
//["firstName", "age"]
|
我们定义给son对象定义了一个不可枚举的属性age,然后通过keys和getOwnPropertyNames两个方法来获取属性列表,能明显看出了两者区别。
属性特性型方法
这个主要是前面提到的三个方法:defineProperty,defineProperties和getOwnPropertyDescriptor三个方法
对象限制型方法
ES5中提供了一系列限制对象被修改的方法,用来防止被某些对象被无意间修改导致的错误。每种限制类型包含一个判断方法和一个设置方法。
阻止对象扩展
Object.preventExtensions()用来限制对象的扩展,设置之后,对象将无法添加新属性,用法如下:
该方法接收一个要被设置成无法扩展的对象作为参数,需要注意两点:
1、对象的属性不可用扩展,但是已存在的属性可以被删除
2、无法添加新属性指的是无法在自身上添加属性,如果是在对象的原型上,还是可以添加属性的。
1
2
3
4
5
6
7
8
9
10
11
|
function
Person(name) {
this
.name = name;
}
var
person =
new
Person(
"Jack"
);
Object.preventExtensions(person);
delete
person.name;
console.log(person.name);
//undefined
Person.prototype.age = 15;
console.log(person.age);
//15
|
Object.isExtensible方法用来判断一个对象是否可扩展,默认情况是true
将对象密封
Object.seal可以密封一个对象并返回被密封的对象。
密封对象无法添加或删除已有属性,也无法修改属性的enumerable,writable,configurable,但是可以修改属性值。
1
2
3
4
5
6
7
|
function
Person(name) {
this
.name = name;
}
var
person =
new
Person(
"Jack"
);
Object.seal(person);
delete
person.name;
console.log(person.name);
//Jack
|
将对象密封后,使用delete删除对象属性,还是可以访问得到属性。
通过Object.isSealed可以用来判断一个对象是否被密封了。
冻结对象
Object.freeze方法用来冻结一个对象,被冻结的对象将无法添加,修改,删除属性值,也无法修改属性的特性值,即这个对象无法被修改。
1
2
3
4
5
6
7
8
9
10
11
|
function
Person(name) {
this
.name = name;
}
var
person =
new
Person(
"Jack"
);
Object.freeze(person);
delete
person.name;
console.log(person.name);
//Jack
Person.prototype.age = 15;
console.log(person.age);
//15
|
分析上面的代码我们可以发现,被冻结的对象无法删除自身的属性,但是通过其原型对象还是可以新增属性的。
通过Object.isFrozen可以用来判断一个对象是否被冻结了。
可以发现:这三个限制对象的方法的限制程度是依次上升的。
总结
Object虽说是一个我们平时开发中最经常用到的对象,但是它的很多功能还没有被我们挖掘出来。本文首先介绍了Object的基本使用,接着介绍了一些比较少使用到的属性特性,最后分析了一些比较常用的方法,尤其是ES5中提供的新方法。欢迎大家交流!!
Symbol类型(引用链接:http://blog.csdn.net/OLiver_web/article/details/54090289)
ES6 中的 Symbol 也是一种数据类型,但是不是字符串,也不是对象,而是一种新的数据类型:第七种数据类型。
下面我们来看一个场景,也许 Symbol 能派上用场。
一个布尔值引出的问题
有时,把一些属于其他对象的数据暂存在另一个对象中是非常方便的。例如,假设你正在编写一个 JS 库,使用 CSS 中的 transition 来让一个 DOM 元素在屏幕上飞奔,你已经知道不能同时将多个 transition 应用在同一个 div 上,否则将使得动画非常不美观,你也确实有办法来解决这个问题,但是首先你需要知道该 div 是否已经在移动中。
怎么解决这个问题呢?
其中一个方法是使用浏览器提供的 API 来探测元素是否处于动画状态,但杀鸡焉用牛刀,在将元素设置为移动时,你的库就知道了该元素正在移动。
你真正需要的是一种机制来跟踪哪些元素正在移动,你可以将正在移动的元素保存在一个数组中,每次要为一个元素设置动画时,首先检查一下这个元素是否已经在这个列表中。
啊哈,但是如果你的数组非常庞大,即便是这样的线性搜索也会产生性能问题。
那么,你真正想做的就是直接在元素上设置一个标志:
1
2
3
4
5
6
7
8
9
10
|
if
(element.isMoving) {
smoothAnimations(element);
}
element.isMoving =
true
;
if
(element.isMoving) {
smoothAnimations(element);
}
element.isMoving =
true
;
|
这也有一些潜在的问题,不得不承认这样一个事实:还有其他代码也可能操作该 ODM 元素。
当然,对于最后三个问题,你可以选择一个无意义的不会有人会使用到的字符串:
1
2
3
4
5
6
7
8
9
|
if
(element.__$jorendorff_animation_library$PLEASE_DO_NOT_USE_THIS_PROPERTY$isMoving__) {
smoothAnimations(element);
}
element.__$jorendorff_animation_library$PLEASE_DO_NOT_USE_THIS_PROPERTY$isMoving__ =
true
;
if
(element.__$jorendorff_animation_library$PLEASE_DO_NOT_USE_THIS_PROPERTY$isMoving__) {
smoothAnimations(element);
}
element.__$jorendorff_animation_library$PLEASE_DO_NOT_USE_THIS_PROPERTY$isMoving__ =
true
;
|
这似乎太不靠谱了,看了让人眼睛痛。
你还可以用加密算法来生成一个几乎唯一的字符串:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// get 1024 Unicode characters of gibberish
var
isMoving = SecureRandom.generateName();
...
if
(element[isMoving]) {
smoothAnimations(element);
}
element[isMoving] =
true
;
// get 1024 Unicode characters of gibberish
var
isMoving = SecureRandom.generateName();
...
if
(element[isMoving]) {
smoothAnimations(element);
}
element[isMoving] =
true
;
|
object[name] 语法允许我们将任何字符串作为属性名,代码能正常工作,冲突几乎是不可能了,代码看起来也美观多了。
但是,这回导致糟糕的调试体验,每次使用 console.log() 打印出包含该属性的元素时,你回看到一个庞大的垃圾字符串,并且如果还不止一个这样的属性呢?每次刷新后属性名都发生了变化,怎么样使这些属性看起来更加直观呢?
为什么这么难?我们只是为了保存一个小小的标志位。
用 Symbol 来解决问题
Symbol 值可以由程序创建,并可以作为属性名,而且不用担心属性名冲突。
1
2
3
|
var
mySymbol = Symbol();
var
mySymbol = Symbol();
|
调用 Symbol() 方法将创建一个新的 Symbol 类型的值,并且该值不与其它任何值相等。
与数字和字符串一样,Symbol 类型的值也可以作为对象的属性名,正是由于它不与任何其它值相等,对应的属性也不会发生冲突:
1
2
3
4
5
|
obj[mySymbol] =
"ok!"
;
// guaranteed not to collide
console.log(obj[mySymbol]);
// ok!
obj[mySymbol] =
"ok!"
;
// guaranteed not to collide
console.log(obj[mySymbol]);
// ok!
|
下面是使用 Symbol 来解决上面的问题:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// create a unique symbol
var
isMoving = Symbol(
"isMoving"
);
...
if
(element[isMoving]) {
smoothAnimations(element);
}
element[isMoving] =
true
;
// create a unique symbol
var
isMoving = Symbol(
"isMoving"
);
...
if
(element[isMoving]) {
smoothAnimations(element);
}
element[isMoving] =
true
;
|
上面代码需要注意几点:
由于 Symbol 的设计初衷是为了避免冲突,当遍历 JavaScript 对象时,并不会枚举到以 Symbol 作为建的属性,比如,for-in 循环只会遍历到以字符串作为键的属性,Object.keys(obj)和 Object.getOwnPropertyNames(obj) 也一样,但这并不意味着 Symbol 为键的属性是不可枚举的:使用 Object.getOwnPropertySymbols(obj) 这个新方法可以枚举出来,还有 Reflect.ownKeys(obj) 这个新方法可以返回对象中所有字符串和 Symbol 键。(我将在后面的文章中详细介绍 Reflect 这个新特性。)
库和框架的设计者将会发现很多 Symbol 的用途,稍后我们将看到,JavaScript 语言本身也对其有广泛的应用。
Symbol 究竟是什么呢
1
2
3
4
5
|
>
typeof
Symbol()
"symbol"
>
typeof
Symbol()
"symbol"
|
Symbol 是完全不一样的东西。一旦创建后就不可更改,不能对它们设置属性(如果在严格模式下尝试这样做,你将得到一个 TypeError)。它们可以作为属性名,这时它们和字符串的属性名没有什么区别。
另一方面,每个 Symbol 都是独一无二的,不与其它 Symbol 重复(即便是使用相同的 Symbol 描述创建),创建一个 Symbol 就跟创建一个对象一样方便。
ES6 中的 Symbol 与传统语言(如 Lisp 和 Ruby)中的 Symbol 中的类似,但并不是完全照搬到 JavaScript 中。在 Lisp 中,所有标识符都是 Symbol;在 JavaScript 中,标识符和大多数属性仍然是字符串,Symbol 只是提供了一个额外的选择。
值得注意的是:与其它类型不同的是,Symbol 不能自动被转换为字符串,当尝试将一个 Symbol 强制转换为字符串时,将返回一个 TypeError。
1
2
3
4
5
6
7
8
9
10
11
|
>
var
sym = Symbol(
"<3"
);
>
"your symbol is "
+ sym
// TypeError: can't convert symbol to string
> `your symbol is ${sym}`
// TypeError: can't convert symbol to string
>
var
sym = Symbol(
"<3"
);
>
"your symbol is "
+ sym
// TypeError: can't convert symbol to string
> `your symbol is ${sym}`
// TypeError: can't convert symbol to string
|
应该避免这样的强制转换,应该使用 String(sym) 或 sym.toString() 来转换。
获取 Symbol 的三种方法
如果你仍不确定 Symbol 是否有用,那么接下来的内容将非常有趣,因为我将为你演示 Symbol 的实际应用。
Symbol 在 ES6 规范中的应用
我们已经知道可以使用 Symbol 来避免代码冲突。之前在介绍 iterator 时,我们还解析了 for (var item of myArray) 内部是以调用 myArray[Symbol.iterator]() 开始的,当时我提到这个方法可以使用 myArray.iterator() 来代替,但是使用 Symbol 的后向兼容性更好。
在 ES6 中还有一些地方使用到了 Symbol。(这些特性还没有在 FireFox 中实现。)
二、JS的原生对象和内置对象
1、原生对象(new后产生的对象)
object in an ECMAScript implementation whose semantics are fully defined by this specification rather than by the host environment.
原生对象:独立于宿主环境的ECMAScript 实现提供的对象。
NOTE Standard native objects are defined in this specification. Some native objects are built-in; others may be constructed during the course of execution of an ECMAScript program.
Object、Function、Array、String、Boolean、Number、Date、RegExp、Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError、ActiveXObject(服务器方面)、Enumerator(集合遍历类)、RegExp(正则表达式)
2、内置对象(不需要new)
object supplied by an ECMAScript implementation, independent of the host environment, that is present at the start of the execution of an ECMAScript program.
内置对象:由ECMAScript实现提供的对象,独立于宿主环境,在一个脚本程序执行的开始处。
NOTE Standard built-in objects are defined in this specification, and an ECMAScript implementation may specify and define others. Every built-in object is a native object. A built-in constructor is a built-in object that is also a constructor.
注:每个内置对象(built-in object
)都是原生对象(Native Object
),一个内置的构造函数是一个内置的对象,也是一个构造函数。
来源:http://es5.github.io/#x4.3.7
ECMA-262 只定义了两个新的内置对象,即 Global
和 Math
(它们也是原生对象,根据定义,每个内置对象都是原生对象)。
内置对象包括:
global、Object、Function、Array、String、Boolean、Number、Math、Date、RegExp、JSON、Error对象(Error, EvalError, RangeError, ReferenceError, SyntaxError, TypeError 和URIError)
math对象:http://www.w3school.com.cn/jsref/jsref_obj_math.asp
Math 对象并不像 Date 和 String 那样是对象的类,因此没有构造函数 Math(),像 Math.sin() 这样的函数只是函数,不是某个对象的方法。您无需创建它,通过把 Math 作为对象使用就可以调用其所有属性和方法。
global对象:http://www.w3school.com.cn/js/jsref_obj_global.asp
全局对象是预定义的对象,作为 JavaScript 的全局函数和全局属性的占位符。通过使用全局对象,可以访问所有其他所有预定义的对象、函数和属性。全局对象不是任何对象的属性,所以它没有名称。
在顶层 JavaScript 代码中,可以用关键字 this 引用全局对象。但通常不必用这种方式引用全局对象,因为全局对象是作用域链的头,这意味着所有非限定性的变量和函数名都会作为该对象的属性来查询。例如,当JavaScript 代码引用 parseInt() 函数时,它引用的是全局对象的 parseInt 属性。全局对象是作用域链的头,还意味着在顶层 JavaScript 代码中声明的所有变量都将成为全局对象的属性。
我们也可以修改内置对象的原型(引用链接https://segmentfault.com/a/1190000002634958)
if (!Array.prototype.forEach) {
Array.prototype.forEach = function(fn){
for ( var i = 0; i < this.length; i++ ) {
fn( this[i], i, this );
}
};
}
["a", "b", "c"].forEach(function(value, index, array){
assert( value, "Is in position " + index + " out of " + (array.length - 1) );
});
以上代码将输出:
PASS Is in position 0 out of 2
PASS Is in position 1 out of 2
PASS Is in position 2 out of 2
注意:扩展原型是很危险的:
Object.prototype.keys = function(){
var keys = [];
for ( var i in this )
keys.push( i );
return keys;
};
var obj = { a: 1, b: 2, c: 3 };
assert( obj.keys().length == 3, "We should only have 3 properties." );
delete Object.prototype.keys;
输出: FAIL We should only have 3 properties.
如果不是有特殊需要而去扩展原生对象和原型(prototype
)的做法是不好的
//不要这样做
Array.prototype.map = function() {
// code
};
除非这样做是值得的,例如,向一些旧的浏览器中添加一些ECMAScript5
中的方法。
在这种情况下,我们一般这样做:
<script type="text/javascript">
if (!Array.prototype.map) {
Array.prototype.map = function() {
//code
};
}
script>
JavaScript
支持四种类型的对象:内部对象、生成的对象、宿主给出的对象(如 Internet 浏览器中的 window
和 document
)以及 ActiveX
对象(外部组件)。
Microsoft Jscript
提供了 11 个内部(或“内置”)对象。它们是Array、Boolean、Date、Function、Global、Math、Number、Object、RegExp、Error
以及 String
对象。每一个对象有相关的方法和属性,
JavaScript
中对象的创建有以下几种方式:
(1)使用内置对象
(2)使用JSON符号
(3)自定义对象构造
JavaScript可用的内置对象可分为两种:
1,JavaScript语言原生对象(语言级对象),如String、Object、Function等;
2,JavaScript运行期的宿主对象(环境宿主级对象),如window、document、body等。
Array
Boolean
Date
Error
EvalError
Function
Infinity
JSON
Map
Math
NaN
Number
Object
ParallelArray
Promise
Proxy
RegExp
Set
String
Symbol
SyntaxError
Uint32Array
WeakSet
decodeURI
decodeURIComponent()
encodeURI()
encodeURIComponent()
escape()已废弃
eval()
isFinite()
isNaN()
null
parseFloat
parseInt
undefined
以上资料来源于:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects#.E5.9F.BA.E6.9C.AC.E5.AF.B9.E8.B1.A1
创建高级对象构造有两种方式:使用“this
”关键字构造、使用原型prototype
构造
getDate() 从 Date 对象返回一个月中的某一天 (1 ~ 31)。
getDay() 从 Date 对象返回一周中的某一天 (0 ~ 6)。
getMonth() 从 Date 对象返回月份 (0 ~ 11)。
getFullYear() 从 Date 对象以四位数字返回年份。注意不要使用getYear()。
getHours() 返回 Date 对象的小时 (0 ~ 23)。
getMinutes() 返回 Date 对象的分钟 (0 ~ 59)。
getSeconds() 返回 Date 对象的秒数 (0 ~ 59)。
getMilliseconds() 返回 Date 对象的毫秒(0 ~ 999)。
getTime() 返回 1970 年 1 月 1 日至今的毫秒数。
getTimezoneOffset() 返回本地时间与格林威治标准时间 (GMT) 的分钟差。
getUTCDate() 根据世界时从 Date 对象返回月中的一天 (1 ~ 31)。
getUTCDay() 根据世界时从 Date 对象返回周中的一天 (0 ~ 6)。
getUTCMonth() 根据世界时从 Date 对象返回月份 (0 ~ 11)。
getUTCFullYear() 根据世界时从 Date 对象返回四位数的年份。
getUTCHours() 根据世界时返回 Date 对象的小时 (0 ~ 23)。
getUTCMinutes() 根据世界时返回 Date 对象的分钟 (0 ~ 59)。
getUTCSeconds() 根据世界时返回 Date 对象的秒钟 (0 ~ 59)。
getUTCMilliseconds() 根据世界时返回 Date 对象的毫秒(0 ~ 999)。
setDate() 设置 Date 对象中月的某一天 (1 ~ 31)。
setMonth() 设置 Date 对象中月份 (0 ~ 11)。
setFullYear() 设置 Date 对象中的年份(四位数字)。注意不要使用setYear()方法。
setHours() 设置 Date 对象中的小时 (0 ~ 23)。
setMinutes() 设置 Date 对象中的分钟 (0 ~ 59)。
setSeconds() 设置 Date 对象中的秒钟 (0 ~ 59)。
setMilliseconds() 设置 Date 对象中的毫秒 (0 ~ 999)。
setTime() 以毫秒设置 Date 对象。
setUTCDate() 根据世界时设置 Date 对象中月份的一天 (1 ~ 31)。
setUTCMonth() 根据世界时设置 Date 对象中的月份 (0 ~ 11)。
setUTCFullYear() 根据世界时设置 Date 对象中的年份(四位数字)。
setUTCHours() 根据世界时设置 Date 对象中的小时 (0 ~ 23)。
setUTCMinutes() 根据世界时设置 Date 对象中的分钟 (0 ~ 59)。
setUTCSeconds() 根据世界时设置 Date 对象中的秒钟 (0 ~ 59)。
setUTCMilliseconds() 根据世界时设置 Date 对象中的毫秒 (0 ~ 999)。
toString() 把 Date 对象转换为字符串,toString()总是返回一个用美式英语表达的字符串。
toTimeString() 把 Date 对象的时间部分转换为字符串。
toDateString() 把 Date 对象的日期部分转换为字符串。
toUTCString() 根据世界时,把 Date 对象转换为字符串。
toLocaleString() 根据本地时间格式,把 Date 对象转换为字符串。
toLocaleTimeString() 根据本地时间格式,把 Date 对象的时间部分转换为字符串。
toLocaleDateString() 根据本地时间格式,把 Date 对象的日期部分转换为字符串。
constructor
指定创建对象原型的函数。
index
如果数组是通过正则表达式匹配创建的,比配是字符串的下标索引为0.
input
如果数组是通过正则表达式匹配创建的,返回原始的字符串。
length长度
返回数组中元素个数.
prototype
允许为所有对象附加属性.
这些方法可以改变数组自身:
pop
移除数组的最后一个元素,返回值是被删除的元素。
push
在数组的末尾添加一个或者多个元素,返回值是新的数组的长度。
reverse
颠倒数组中元素的顺序,原先第一个元素现在变成最后一个,同样原先的最后一个元素变成了现在的第一个,也就是数组的索引发生了变化。
shift
删除数组的第一个元素,返回值是删除的元素。
sort
对数组中的元素进行排序。
splice
添加或删除数组中的一个或多个元素。
unshift
添加一个或者多个元素在数组的开头,返回值是新的数组的长度。
这些过程不改变数组自身
concat
返回一个包含此数组和其他数组和/或值的结合的新数组
indexOf
返回第一个与给定参数相等的数组元素的索引,没有找到则返回-1。
join
将所有的数组元素连接成一个字符串。
lastIndexOf
返回在数组中搜索到的与给定参数相等的元素的最后(最大)索引。
slice
返回数组中的一段。
toSource
Returns an array literal representing the specified array; you can use this value to create a new array. Overrides the Object.toSource method.
toString
返回代表该数组及其元素的字符,重写Object.toString 过程.
valueOf
Returns the primitive value of the array. Overrides the Object.valueOf method.
filter
对数组中的每一个元素调用参数中指定的过滤函数,并将对于过滤函数返回值为true的那些数组元素集合为新的数组返回。
forEach
对数组的每一个元素依次调用参数中指定的函数。
every
如果数组中每一个元素都满足参数中提供的测试函数,则返回真。
map
Creates a new array with the results of calling a provided function on every element in this array.
some
如果数组中至少有一个元素满足参数函数的测试,则返回true。
改变原数组的方法:pop()、push()、reverse()、shift()、sort()、splice()、unshift()
不改变原数组的方法:concat()、join()、slice()、toString()
Boolean.length
长度属性,值为1.
Boolean.prototype
代表Boolean构造器的原型.
Boolean.prototype.toSource()
Boolean.prototype.toString()
Boolean.prototype.valueOf()
String.length
String.prototype
charAt() 方法可返回指定位置的字符。stringObject.charAt(index)
charCodeAt() 方法可返回指定位置的字符的 Unicode 编码。这个返回值是 0 – 65535 之间的整数。 stringObject.charCodeAt(index)
concat() 方法用于连接两个或多个字符串。
stringObject.concat(stringX, stringX, …, stringX)
indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。
stringObject.indexOf(searchvalue, fromindex)
lastIndexOf() 方法可返回一个指定的字符串值最后出现的位置,在一个字符串中的指定位置从后向前搜索。
localeCompare():用本地特定的顺序来比较两个字符串stringObject.localeCompare(target)
match() 方法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配。
该方法类似 indexOf() 和 lastIndexOf(),但是它返回指定的值,而不是字符串的位置。
stringObject.match(regexp)
replace() 方法用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。stringObject.replace(regexp/substr, replacement)
search() 方法用于检索字符串中指定的子字符串,或检索与正则表达式相匹配的子字符串。
stringObject.search(regexp)
slice() 方法可提取字符串的某个部分,并以新的字符串返回被提取的部分。
stringObject.slice(start, end)
substring()不推荐使用,建议使用slice()替代。
substr()不推荐使用,建议使用slice()替代。
toLocaleLowerCase()不推荐使用,只在土耳其语等少数语种中有用,建议使用toLowerCase()替代。
toLocaleUpperCase()不推荐使用,只在土耳其语等少数语种中有用,建议使用toUpperCase()替代。
toLowerCase() 方法用于把字符串转换为小写。
toUpperCase() 方法用于把字符串转换为大写。
注意:String
对象的方法 slice()、substring()
和 substr()
都可返回字符串的指定部分。强烈建议在所有场合都使用 slice()
方法。
RegExp.prototype.constructor
创建该正则对象的构造函数。
RegExp.prototype.global
是否开启全局匹配,也就是匹配目标字符串中所有可能的匹配项,而不是只进行第一次匹配。
RegExp.prototype.ignoreCase
在匹配字符串时是否要忽略字符的大小写。
RegExp.prototype.lastIndex
下次匹配开始的字符串索引位置。
RegExp.prototype.multiline
是否开启多行模式匹配(影响 ^ 和 $ 的行为)
RegExp.prototype.source
正则对象的源模式文本。
RegExp.prototype.exec()
在目标字符串中执行一次正则匹配操作。
RegExp.prototype.test()
测试当前正则是否能匹配目标字符串。
RegExp.prototype.toString()
返回一个字符串,其值为该正则对象的字面量形式。覆盖了Object.prototype.toString() 方法。
当以非构造函数形式被调用时,Object
等同于 new Object()
。
Object
的每个实例都具有共同的基本属性和方法
属性或者方法 | 说明 |
---|---|
constructor | 指向创建当前对象的构造函数 |
hasOwnProperty(name) | 检测给定属性name在实例对象(不是原型对象)中是否存在。name以字符串形式指定 |
isPropertyOf(object) | 检测传入的对象object是否该方法调用者的原型对象。一般格式:Class.prototype.isPropertyOf(object) |
propertyIsEnumerable(pr) | 检测属性pr能否用for-in循环枚举。属性pro用字符串形式指定 |
toLocaleString() | 返回对象的字符串表示。与地区和环境对应 |
toString() | 返回对象的字符串表示 |
valueOf() | 返回对象的字符串、数值或布尔值表示 |
Object.create(prototype, descriptors)
以指定的原型创建对象,并且可以(可选)的设置对象的属性
Object.defineProperty(object, propertyname, descriptor)
对指定的对象的一个属性设置丰富的值控制
Object.defineProperties(object, descriptors)
对指定的对象的一组属性提供丰富的值控制
Object.getOwnPropertyDescriptor(object, propertyname)
返回属性的定义
Object.getOwnPropertyNames(object)
返回所有属性的名称,哪怕说是不能枚举的属性
Object.preventExtensions(object)
防止新的属性添加到对象
Object.isExtensible(object)
是否可添加属性到对象
Object.seal(object)
阻止向指定对象添加新属性或删除现有属性
Object.freeze(object)
防止现有属性和属性值的修改,并防止新特性的添加。
Object.isFrozen(object)
检查当前对象是否已冻结
Object.getPrototypeOf(object)
返回指定对象的原型