JavaScript深入浅出(1)

数据类型:

6种基本数据类型:

  • number
  • string
  • boolean
  • null
  • undefined
  • object

包装对象

理解:基本类型String/Number/Boolean是没有属性的,但有对应的包装对象String/Number/Boolean,当对其进行访问和设置属性操作时,js会将其临时转换为对应的包装对象类型String/Number/Boolean,这样就可以访问或设置属性了。然后其属性会被销毁。
如下:
var a = “string”; //基础类型
alert(a.length); //6,访问length属性时a被转换为包装对象类型
a.t = 3;//添加t属性,这时a被转换为包装对象类型
alert(a.t); //undefined,由于上面的操作已完成,临时对象被销毁,a是基础类型,没有属性

类型检测方法:

typeof

返回字符串,适合函数(function)对象和基本类型的判断检测,遇到null失效

1
2
3
4
5
6
7
8
typeof 100 "number"//返回字符串number
typeof true "boolean"
typeof function "function"
typeof (undefined) "undefined"
typeof new Object() "object"
typeof [1,2] "object"//没有经过特殊处理
typeof NaN "number"
typeof null "object"//兼容问题

instanceof

:适用于对象类型,基于原型链来判断类型,也可以检测原生对象
obj instanceof Object//基于原型链
会判断左操作数在原型链上是否有右边构造函数的prototype属性
任何一个构造函数都有一个prototype属性
不同window或iframe间的对象类型检测不能使用instanceof

Object.prototype.toString

遇到null,undefiend失效
Object.prototype.toString.apply([]);===”[object Array]”//判断数组

constructor

任何一个对象都有一个constructor属性,指向构造这个对象的构造器或者构造函数,可以被改写。

duck type

比如不知道这个是不是数组,可以判断他的特征:length数字,是否有join,push等等

任务:arraysSimilar函数,实现判断传入的两个数组是否相似。具体需求:

  1. 数组中的成员类型相同,顺序可以不同。例如[1, true] 与 [false, 2]是相似的。
  2. 数组的长度一致。
  3. 类型的判断范围,需要区分:String, Boolean, Number, undefined, null, 函数,日期, window.
    当以上全部满足,则返回”判定结果:通过”,否则返回”判定结果:不通过”。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    function type(a){
    return Object.prototype.toString.apply(a); //hack ie678
    }
    function arraysSimilar(arr1, arr2){
    if(!Array.isArray(arr1) || !Array.isArray(arr2) ||arr1.length!=arr2.length){return false;}
    var arr3=[];
    var arr4=[];
    var x;
    for(var i in arr1){
    arr3.push(type(arr1[i]));
    arr4.push(type(arr2[i]));
    }
    if(arr3.sort().toString()==arr4.sort().toString()){
    return true;
    }else{
    return false;
    }
    }

表达式与运算符

属性访问表达式

var o = {x : 1};
o.x
o[‘x’]

1
val = +val;
实际上就是利用一元+操作符的特性(会尝试转换为数字),来隐式将一个变量转换为数字类型(number)。
例如:
如果val是”123”
+”123”; // 输出123,类型是number,而不再是string

,运算符:

var val = (1, 2, 3); // val = 3

运算符 delete

1
2
3
4
5
6
7
8
9
10
11
12
var obj = {x : 1};
obj.x; // 1
delete obj.x;
obj.x; // undefined

var obj = {};
Object.defineProperty(obj, 'x', {
configurable : false,
value : 1
});
delete obj.x; // false
obj.x; // 1

运算符 void

1
2
void 0  // undefined
void(0) // undefined

运算符优先级

语句

block:

没有块级作用域

1
2
3
4
5
6
7
8
9
10
11
// 下面两种写法相等
for (var i = 0; i < 10; i++) {
var str = "hi";
console.log(str);
}

var i = 0
for (; i < 10; i++) {
var str = "hi";
console.log(str);
}

var

如下例,b未定义,为全局变量,a为局部变量。

1
2
3
4
5
6
7
function foo() {
var a = b = 1;
}
foo();

console.log(typeof a); // ‘undefined’
console.log(typeof b); // ‘number’

try catch语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
try {
throw "test";
} catch (ex) {
console.log(ex); // test
} finally {
console.log('finally');
}

// 下面是几种try catch语句的形式/嵌套
try {
// do sth.
} finally {
console.log('finally');
}

/* try catch嵌套try catch时,无论是否抛出错误,都会首先执行内部的finally语句。
结果为:
"finally"
"outer" "oops"
*/

try {
try {
throw new Error("oops");
}
finally {
console.log("finally");
}
}
catch (ex) {
console.error("outer", ex.message);
}

/* 结果为:
"inner" "oops"
"finally"
*/

try {
try {
throw new Error("oops");
}
catch (ex) {
console.error("inner", ex.message);
}
finally {
console.log("finally");
}
}
catch (ex) {
console.error("outer", ex.message);
}

/* 结果为:
"inner" "oops"
"finally"
"outer" "oops"
*/

try {
try {
throw new Error("oops");
}
catch (ex) {
console.error("inner", ex.message);
throw ex;
}
finally {
console.log("finally");
}
}
catch (ex) {
console.error("outer", ex.message);
}

for…in

  1. 顺序不确定
  2. enumerable为false时不会出现
  3. for in对象属性时受原型链影响
    1
    2
    3
    4
    5
    var p;
    var obj = {x : 1, y: 2}

    for (p in obj) {
    }

with语句

  • 让JS引擎优化更难
  • 可读性差
  • 可被变量定义代替
  • 严格模式下被禁用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    with ({x : 1}) {
    console.log(x);
    } //输出1

    with (document.forms[0]) {
    console.log(name.value);
    }
    //等价于:
    var form = document.forms[0];
    console.log(form.name.value);

严格模式

严格模式是一种特殊的执行模式,
它修复了部分语言上的不足,提供更强的错误检查,并增强安全性。

1
2
3
4
5
6
7
8
function func() {
'use strict';
}
//或者如下格式:
'use strict';
function func() {

}

‘use strict’;指令并不一定要在第一行出现的,之前也可以有其它指令(字符串)。
但是不允许有其它语句,如变量声明、赋值等。

例如下面例子中:

1
2
3
4
5
!function() {
'abc';
'use strict';
console.log(this === undefined ? "strict" : "not strict");
}();

上面的use strict前面有abc,仍将进入严格模式,下面的不会进入严格模式。

1
2
3
4
5
!function() {
var a;
'use strict';
console.log(this === undefined ? "strict" : "not strict");
}();

严格模式与普通模式区别:

  • 不允许用with
  • 所有变量必须声明, 赋值给未声明的变量报错,而不是隐式创建全局变量。
  • eval中的代码不能创建eval所在作用域下的变量、函数。而是为eval单独创建一个作用域,并在eval返回时丢弃。
  • 函数中的特殊对象arguments是静态副本,而不像非严格模式那样,修改arguments或修改参数变量会相互影响。
  • 删除configurable=false的属性时报错,而不是忽略
  • 禁止八进制字面量,如010 (八进制的8)
  • eval, arguments变为关键字,不可作为变量名、函数名等
  • 一般函数调用时(不是对象的方法调用,也不使用apply/call/bind等修改this)this指向null,而不是全局对象。
  • 若使用apply/call,当传入null或undefined时,this将指向null或undefined,而不是全局对象。
  • 试图修改不可写属性(writable=false),在不可扩展的对象上添加属性时报TypeError,而不是忽略。
  • arguments.caller, arguments.callee被禁用

2-10
2-10
2-10
2-10
2-10
2-10
2-10
2-10
2-10

对象

对象中包含一系列属性,这些属性是无序的。
每个属性都有一个字符串key和对应的value。

1
2
3
4
5
6
7
8
var obj = {};
obj[1] = 1; //此时obj={1:1}
obj['1'] = 2;//替换上一个属性值
obj; // Object {1: 2}

obj[{}] = true;
obj[{x : 1}] = true;
obj; // Object {1: 2, [object Object]: true}

对象结构

7.30.1

对象创建

三种方法:
1、对象字面量

1
2
3
4
5
6
7
8
var obj2 = {
x : 1,
y : 2,
o : {
z : 3,
n : 4
}
};

2、new/原型链

1
2
3
4
5
6
7
8
9
10
function foo(){}
foo.prototype.z = 3;

var obj =new foo();
obj.y = 2;
obj.x = 1;
obj.toString //结果为:function toString() { [native code] }
typeof obj.toString; //结果为:function
'z' in obj; // true, in语句时,对象属性会受原型链影响。
obj.hasOwnProperty('z'); // false

3、Object.create
2

1
2
3
4
5
6
7
var obj = Object.create({x : 1});
obj.x // 1
typeof obj.toString // "function"
obj.hasOwnProperty('x');// false

var obj = Object.create(null);
obj.toString // undefined

属性操作

属性检测

1
2
3
4
5
6
7
Object.defineProperty(cat, 'price', {enumerable : false, value : 1000});
cat.propertyIsEnumerable('price'); // false
cat.hasOwnProperty('price'); // true

if (cat.legs != undefined) {
// !== undefined, or, !== null
} // cat.legs!=undefined等价于 !==undefined || !==null

属性枚举

1
2
3
4
5
6
7
var o = {x : 1, y : 2, z : 3};
'toString' in o; // true
o.propertyIsEnumerable('toString'); // false
var key;
for (key in o) {
console.log(key); // x, y, z
}

get/set –读写属性的另一种方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var man = {
weibo : '@Bosn',
$age : null, //此处$age为临时变量,可写成_age,也可以不写。
get age() {
if (this.$age == undefined) {
return new Date().getFullYear() - 1988;
} else {
return this.$age;
}
},
set age(val) {
val = +val;
if (!isNaN(val) && val > 0 && val < 150) {
this.$age = +val;
} else {
throw new Error('Incorrect val = ' + val);
}
}
}
console.log(man.age); // 28
man.age = 100;
console.log(man.age); // 100;
man.age = 'abc'; // error:Incorrect val = NaN
get/set与原型链
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//通过new创建对象
function foo() {}
Object.defineProperty(foo.prototype, 'z',
{get : function(){return 1;}});
var obj = new foo();
obj.z; // 1
obj.z = 10;
obj.z; // still 1

Object.defineProperty(obj, 'z',
{value : 100, configurable: true});
obj.z; // 100;
delete obj.z;
obj.z; // back to 1

//通过Object.create创建对象
var o = {};
Object.defineProperty(o, 'x', {value : 1}); // writable=false, configurable=false
var obj = Object.create(o);
obj.x; // 1
obj.x = 200;
obj.x; // still 1, can't change it

Object.defineProperty(obj, 'x', {writable:true, configurable:true, value : 100});
obj.x; // 100
obj.x = 500;
obj.x; // 500

属性标签

属性权限设置

Object.getOwnPropertyDescriptor(obj,’key’):读取对象obj的key权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Object.getOwnPropertyDescriptor({pro : true}, 'pro');
// Object {value: true, writable: true, enumerable: true, configurable: true}
Object.getOwnPropertyDescriptor({pro : true}, 'a'); // undefined

//通过Object.defineProperty(obj,'key')可以设置属性权限
var person = {};
Object.defineProperty(person, 'name', {
configurable : false,
writable : false,
enumerable : true,
value : "Bosn Ma"
});
person.name; // Bosn Ma
person.name = 1;
person.name; // still Bosn Ma
delete person.name; // false

//通过Object.defineProperty(obj,{key1:{value:},key2:{value:},})可以设置多个属性权限
Object.defineProperties(person, {
title : {value : 'fe', enumerable : true},
corp : {value : 'BABA', enumerable : true},
salary : {value : 50000, enumerable : true, writable : true}
});

Object.getOwnPropertyDescriptor(person, 'salary');
// Object {value: 50000, writable: true, enumerable: true, configurable: false}
Object.getOwnPropertyDescriptor(person, 'corp');
// Object {value: "BABA", writable: false, enumerable: true, configurable: false}

3

对象标签

  • [[proto]]
  • [[class]]
  • [[extensible]]
    class标签
    toString = Object.prototype.toString;
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function getType(o){return toString.call(o).slice(8,-1);};

    toString.call(null); // "[object Null]"
    getType(null); // "Null"
    getType(undefined); // "Undefined"
    getType(1); // "Number"
    getType(new Number(1)); // "Number"
    typeof new Number(1); // "object"
    getType(true); // "Boolean"
    getType(new Boolean(true)); // "Boolean"
extensible标签
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var obj = {x : 1, y : 2};
Object.isExtensible(obj); // true
Object.preventExtensions(obj);
Object.isExtensible(obj); // false
obj.z = 1;
obj.z; // undefined, add new property failed
Object.getOwnPropertyDescriptor(obj, 'x');
// Object {value: 1, writable: true, enumerable: true, configurable: true}

Object.seal(obj);
Object.getOwnPropertyDescriptor(obj, 'x');
// Object {value: 1, writable: true, enumerable: true, configurable: false}
Object.isSealed(obj); // true

Object.freeze(obj);
Object.getOwnPropertyDescriptor(obj, 'x');
// Object {value: 1, writable: false, enumerable: true, configurable: false}
Object.isFrozen(obj); // true

// [caution] not affects prototype chain!!!

对象序列化方法

1
2
3
4
5
6
7
8
9
var obj = {x : 1, y : true, z : [1, 2, 3], nullVal : null};
JSON.stringify(obj); // "{"x":1,"y":true,"z":[1,2,3],"nullVal":null}"

//序列化时,属性值为undefined时自动忽略,属性值为NaN/Infinity时会变成null.
obj = {val : undefined, a : NaN, b : Infinity, c : new Date()};
JSON.stringify(obj); // "{"a":null,"b":null,"c":"2015-01-20T14:15:43.910Z"}"

obj = JSON.parse('{"x" : 1}');
obj.x; // 1

其他方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var obj = {x:1, y:2};
obj.toString(); // "[object Object]"
obj.toString = function(){return this.x + this.y;};
"Result " + obj; // "Result 3", by toString
obj.valueOf = function(){return this.x + this.y + 100}
"result" + obj; // 这里是result103,而不是result3
/*
JavaScript中的二元+操作符,若操作数为对象,则尝试转换为基本类型。优先级是先找valueOf,再找toString。
注意到,若valueOf/toString返回的不是基本类型,而是对象,则会被忽略。
*/

var obj = {x:1, y:2};
obj.toString = function(){return this.x + this.y;};
obj.valueOf = function(){return {x : 1}}; // 不可用的valueOf
"result" + obj; // "result3", 因为valueOf无效,使用toString作为结果返回

//若valueOf/toString均不可用,则报TypeError异常。
var obj = {x:1, y:2};
obj.toString = function(){return {};}; // 不可用的toString
obj.valueOf = function(){return {x : 1}}; // 不可用的valueOf
"result" + obj; // Uncaught TypeError: Cannot convert object to primitive value

数组

数组元素增删

添加数组元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var arr = [];
arr[0] = 1;
arr[1] = 2;

// 在数组末尾添加新元素-push()

// push方法
arr.push(3);
arr; // [1, 2, 3]

//给数组长度复制
arr[arr.length] = 4; // equal to arr.push(4);
arr; // [1, 2, 3, 4]

//在数组头部添加新元素--unshift()

// unshift方法
arr.unshift(0);
arr; // [0, 1, 2, 3, 4];
删除数组元素–delete/pop()/shift()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// delete方法
delete arr[2];
arr; // [0, 1, undefined, 3, 4]
arr.length; // 5
2 in arr; // false

//数组长度自减一
arr.length -= 1;
arr; // [0, 1, undefined, 3, 4], 4 is removed

// pop()方法--在数组末尾删除元素
arr.pop(); // 3 returned by pop
arr; // [0, 1, undefined], 3 is removed

// shift()方法--数组头部删除元素
arr.shift(); // 0 returned by shift
arr; // [1, undefined]

稀疏数组

稀疏数组是不含有从0开始的连续索引。一般length属性值比实际元素个数大。
如果那个位置有值的话用in判断就返回true,反之则返回false。

1
2
3
4
5
6
7
8
9
10
11
12
var arr1 = [undefined];
var arr2 = new Array(1);
0 in arr1; // true
0 in arr2; // false
arr1.length = 100;
arr1[99] = 123;
99 in arr1; // true
98 in arr1; // false, 虽然通过arr[99]使得数组长度变为100,但JavaScript并不会自动创建从0 ~ 98的其它Key。
这就是稀疏数组。

var arr = [,,];
0 in arr; // false

数组方法

4

Array.prototype.join–将数组转为字符串
1
2
3
4
5
6
7
8
9
10
var arr = [1, 2, 3];
arr.join(); // "1,2,3"
arr.join("_"); // "1_2_3"

//n次重复字符串方法
function repeatString(str, n) {
return new Array(n + 1).join(str);
}
repeatString("a", 3); // "aaa"
repeatString("Hi", 5); // "HiHiHiHiHi"
Array.prototype.reverse–将数组逆序(原数组被修改)
1
2
3
var arr = [1, 2, 3];
arr.reverse(); // [3, 2, 1]
arr; // [3, 2, 1]
Array.prototype.sort–数组排序(原数组被修改)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var arr = ["a", "d", "c", "b"];
arr.sort(); // ["a", "b", "c", "d"]

arr = [13, 24, 51, 3];
arr.sort(); // [13, 24, 3, 51]
arr; // [13, 24, 3, 51]

arr.sort(function(a, b) {
return a - b;
}); // [3, 13, 24, 51]
/*若 a 小于 b,在排序后的数组中 a 应该出现在 b 之前,则返回一个小于 0 的值。
若 a 等于 b,则返回 0。
若 a 大于 b,则返回一个大于 0 的值。*/


arr = [{age : 25}, {age : 39}, {age : 99}];
arr.sort(function(a, b) {
return a.age - b.age;
});
arr.forEach(function(item) {
console.log('age', item.age);
});
// result:
// age 25
// age 39
// age 99
Array.prototype.concat–数组合并(原数组未被修改)
1
2
3
4
5
6
7
var arr = [1, 2, 3];
arr.concat(4, 5); // [1, 2, 3, 4, 5]
arr; // [1, 2, 3]

arr.concat([10, 11], 13); // [1, 2, 3, 10, 11, 13]
//如果合并的数组里嵌套数组,则嵌套的数组不会合并
arr.concat([1, [2, 3]]); // [1, 2, 3, 1, [2, 3]]
Array.prototype.slice–返回部分数组(原数组未被修改)
1
2
3
4
5
var arr = [1, 2, 3, 4, 5];
arr.slice(1, 3); // [2, 3]
arr.slice(1); // [2, 3, 4, 5]
arr.slice(1, -1); // [2, 3, 4]
arr.slice(-4, -3); // [2]
Array.prototype.splice–数组拼接(原数组被修改)
1
2
3
4
5
6
7
8
9
10
11
var arr = [1, 2, 3, 4, 5];
arr.splice(2); // returns [3, 4, 5]
arr; // [1, 2];

arr = [1, 2, 3, 4, 5];
arr.splice(2, 2); // returns [3, 4]
arr; // [1, 2, 5];

arr = [1, 2, 3, 4, 5];
arr.splice(1, 1, 'a', 'b'); // returns [2]
arr; // [1, "a", "b", 3, 4, 5]
Array.prototype.forEach–数组遍历
1
2
3
4
5
6
7
8
9
var arr = [1, 2, 3, 4, 5];
arr.forEach(function(x, index, a){
console.log(x + '|' + index + '|' + (a === arr));
});
// 1|0|true
// 2|1|true
// 3|2|true
// 4|3|true
// 5|4|true
Array.prototype.map–数组映射(原数组未被修改)
1
2
3
4
5
var arr = [1, 2, 3];
arr.map(function(x) {
return x + 10;
}); // [11, 12, 13]
arr; // [1, 2, 3]
Array.prototype.filter–数组过滤(原数组未被修改)
1
2
3
4
5
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
arr.filter(function(x, index) {
return index % 3 === 0 || x >= 8;
}); // returns [1, 4, 7, 8, 9, 10]
arr; // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Array.prototype.every & some–数组判断
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var arr = [1, 2, 3, 4, 5];
arr.every(function(x) {
return x < 10;
}); // true

arr.every(function(x) {
return x < 3;
}); // false
var arr = [1, 2, 3, 4, 5];
arr.some(function(x) {
return x === 3;
}); // true

arr.some(function(x) {
return x === 100;
}); // false

var arr = [1, 2, 3, 4, 5];
arr.some(function(x) {
return x === 3;
}); // true

arr.some(function(x) {
return x === 100;
}); // false
Array.prototype.reduce&reduceRight–(原数组未被修改)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var arr = [1, 2, 3];
var sum = arr.reduce(function(x, y) {
return x + y
}, 0); // 6
arr; //[1, 2, 3]

arr = [3, 9, 6];
var max = arr.reduce(function(x, y) {
console.log(x + "|" + y);
return x > y ? x : y;
});
// 3|9
// 9|6
max; // 9

max = arr.reduceRight(function(x, y) {
console.log(x + "|" + y);
return x > y ? x : y;
});
// 6|9
// 9|3
max; // 9
Array.prototype.indexOf&lastIndexOf–数组检索
1
2
3
4
5
6
7
8
9
var arr = [1, 2, 3, 2, 1];
arr.indexOf(2); // 1
arr.indexOf(99); // -1
arr.indexOf(1, 1); // 4
arr.indexOf(1, -3); // 4
arr.indexOf(2, -1); // -1
arr.lastIndexOf(2); // 3
arr.lastIndexOf(2, -2); // 3
arr.lastIndexOf(2, -3); // 1
Array.isArray–判断是否为数组
1
2
3
4
5
Array.isArray([]); // true
//其他方法:
[] instanceof Array; // true
({}).toString.apply([]) === '[object Array]'; // true
[].constructor === Array; // true

数组与一般对象区别:

相同点:

都可以继承
数组是对象,对象不一定是数组
都可以当做对象添加删除属性
不同点:
数组自动更新length
按索引访问数组常常比访问一般对象属性明显迅速。
数组对象继承Array.prototype上的大量数组操作方法

坚持原创技术分享,您的支持将鼓励我继续创作!