一、对象方法

1.1、Object.keys()

1.1.1、概述

1
2
3
4
5
6
# 语法
Object.keys(obj)
# 含义
用于获取对象自身所有的属性
1. 效果类似 for…in
2. 返回一个由属性名组成的数组

1.1.2、案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<body>
<script>
var person = {
id: 1,
uname: 'HelloWorld',
age: 23
};

var keys = Object.keys(person);
console.log(keys); // ['id', 'uname', 'age']

// 遍历key的集合
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
console.log(person[key]);
}
</script>
</body>

1.2、Object.defineProperty()

1.2.1、概述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 语法
Object.defineProperty(obj, prop, descriptor)
# 含义
定义对象中新属性或修改原有的属性。
1、参数一obj:
必需。目标对象

2、参数二prop:必需。
需定义或修改的属性的名字

3、参数三descriptor:必需。目标属性所拥有的特性,该参数是一个对象的形式表示。
value: 设置属性的值 默认为undefined
writable: 值是否可以重写。true | false 默认为false
enumerable: 目标属性是否可以被枚举。true | false 默认为 false
configurable: 目标属性是否可以被删除或是否可以再次修改特性 true | false 默认为false

1.2.2、案例1

​ 在原有对象身上添加新属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<body>
<script>
var person = {
id: 1,
uname: 'HelloWorld',
age: 23
};

//
Object.defineProperty(person, 'address', {
value: '浙江省'
});

console.log(person); // {id: 1, uname: 'HelloWorld', age: 23, address: '浙江省'}
</script>
</body>

1.2.3、案例2

​ 修改原对象身上的uname属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<body>
<script>
var person = {
id: 1,
uname: 'HelloWorld',
age: 23
};

Object.defineProperty(person, 'uname', {
value: 'JavaScript'
});

console.log(person); // {id: 1, uname: 'JavaScript', age: 23}
</script>
</body>

1.2.4、案例3

​ 设置属性不能修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<body>
<script>
var person = {
id: 1,
uname: 'HelloWorld',
age: 23
};

// 设置 uname 属性不可修改
Object.defineProperty(person, 'uname', {
writable: false
});

// 发现 uname 是不能修改的
person.uname = 'HTML';

console.log(person); // {id: 1, uname: 'HelloWorld', age: 23}
</script>
</body>

1.2.5、案例4

enumerable : 目标属性是否可以被枚举。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<body>
<script>
var person = {
id: 1,
uname: 'HelloWorld',
age: 23
};

// 添加 address 属性
Object.defineProperty(person, 'address', {
value: '杭州市',
// 目标属性是否可以被枚举,false表示不可被枚举,默认是false
enumerable: false
});

console.log(Object.keys(person)); // ['id', 'uname', 'age']
</script>
</body>

1.2.6、案例5

configurable 如果为false 则不允许删除这个属性 默认为false。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<body>
<script>
var person = {
id: 1,
uname: 'HelloWorld',
age: 23
};

// configurable 如果为false 则不允许删除这个属性 默认为false
Object.defineProperty(person, 'uname', {
configurable: false
});

// 删除 uname 属性
delete person.uname;

// 发现并没有删除 uname 属性
console.log(Object.keys(person)); // ['id', 'uname', 'age']
</script>
</body>

二、函数的定义和调用

2.1、函数的定义方式

2.1.1、自定义函数/命名函数

1
2
3
4
5
6
7
8
9
<body>
<script>
// 函数的定义方式
// >> 自定义函数/命名函数
function fn() {
console.log('--fn--');
}
</script>
</body>

2.1.2、函数表达式/匿名函数

1
2
3
4
5
6
7
8
9
<body>
<script>
// 函数的定义方式
// >> 函数表达式 (匿名函数)
var fn = function() {
console.log('--fn--');
}
</script>
</body>

2.1.3、利用 new Function

1
2
3
# 语法
new Function('参数1','参数2', '函数体');
1. Function 里面参数都必须是字符串格式
1
2
3
4
5
6
7
8
<body>
<script>
// 函数的定义方式
// >> new Function('参数1','参数2', '函数体');
var fn = new Function("console.info('HelloWorld')");
fn();
</script>
</body>
1
2
3
4
5
6
7
8
<body>
<script>
// 函数的定义方式
// >> new Function('参数1','参数2', '函数体');
var fn = new Function("a", "b", "console.info(a + b)");
fn(10, 20);
</script>
</body>
1
2
3
4
5
6
7
8
<body>
<script>
// 函数的定义方式
// >> new Function('参数1','参数2', '函数体');
var fn = new Function("a", "b", "console.info(a + b)");
console.log(fn instanceof Object); // true
</script>
</body>
1
2
3
# 说明
1. 所有函数都是 Function 的实例(对象)
2. 函数也属于对象

2.2、函数的调用方式

2.2.1、普通函数

1
2
3
4
5
6
7
8
9
10
<body>
<script>
// 函数的定义方式
// >> 普通函数调用
function fn() {
console.log('--fn--');
}
fn();
</script>
</body>
1
2
3
4
5
6
7
8
9
10
<body>
<script>
// 函数的定义方式
// >> 普通函数调用
function fn() {
console.log('--fn--');
}
fn.call();
</script>
</body>

2.2.2、对象的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
<body>
<script>
// 函数的定义方式
// >> 对象的方法
var person = {
run: function() {
console.log('--run--');
}
}

person.run();
</script>
</body>

2.2.3、构造函数

1
2
3
4
5
6
7
8
9
10
11
<body>
<script>
// 函数的定义方式
// >> 构造函数
function Person() {
console.log('---构造函数---');
}

new Person();
</script>
</body>

2.2.4、绑定事件函数

1
2
3
4
5
6
7
8
9
10
11
12
<body>
<button>注册</button>
<script>
// 函数的定义方式
// >> 绑定事件函数
var btnObj = document.querySelector('button');

btnObj.onclick = function() {
console.log('--注册--');
}
</script>
</body>

2.2.5、定时器函数

1
2
3
4
5
6
7
8
9
<body>
<script>
// 函数的定义方式
// >> 定时器函数
setTimeout(function() {
console.log('---定时器函数---');
}, 2000);
</script>
</body>

2.2.6、立即执行函数

1
2
3
4
5
6
7
8
9
<body>
<script>
// 函数的定义方式
// >> 立即执行函数
(function(num) {
console.log('--' + num);
})(30);
</script>
</body>

2.2.7、总结

1
2
3
4
5
6
7
# 函数的调用方式    
1. 普通函数
2. 对象的方法
3. 构造函数
4. 绑定事件函数
5. 定时器函数
6. 立即执行函数

三、this关键字

3.1、函数内 this 的指向

3.1.1、说明

​ 这些 this 的指向,是当我们调用函数的时候确定的。 调用方式的不同决定了this 的指向不同。通常来说,一般指向我们的调用者。

3.1.2、图示

0124

3.1.3、普通函数调用

1
2
3
4
5
6
7
8
9
10
<body>
<script>
// 函数的调用,this 的指向问题
// >> 普通函数调用
function fn() {
console.log('普通函数的this-->', this);
}
fn();
</script>
</body>
1
# 普通函数 this 指向window

3.1.4、对象方法调用

1
2
3
4
5
6
7
8
9
10
11
12
<body>
<script>
// 函数的调用,this 的指向问题
// >> 对象方法调用
var person = {
run: function() {
console.log('对象方法的this', this);
}
};
person.run();
</script>
</body>
1
#  对象的方法 this指向的是对象 person

3.1.5、构造函数调用

1
2
3
4
5
6
7
8
9
10
<body>
<script>
// 函数的调用,this 的指向问题
// >> 构造方法调用
function Star() {
console.log('构造方法的this-->', this);
}
var zhangsan = new Star();
</script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<body>
<script>
// 函数的调用,this 的指向问题
// >> 构造方法调用
function Star() {
console.log('构造函数的this', this);
}
Star.prototype.run = function() {
console.log('方法', this);
}
var zhangsan = new Star();
zhangsan.run();
</script>
</body>
1
# 构造函数 this 指向 zhangsan 这个实例对象 原型对象里面的this 指向的也是 zhangsan 这个实例对象。

3.1.6、绑定事件函数调用

1
2
3
4
5
6
7
8
9
10
11
<body>
<button>注册</button>
<script>
// 函数的调用,this 的指向问题
// >> 绑定事件函数调用
var btnObj = document.querySelector('button');
btnObj.onclick = function() {
console.log('注册事件处理函数的this', this);
}
</script>
</body>
1
# 绑定事件函数 this 指向的是函数的调用者 button 这个按钮对象

3.1.7、定时器函数调用

1
2
3
4
5
6
7
8
9
<body>
<script>
// 函数的调用,this 的指向问题
// >> 定时器调用
setTimeout(function() {
console.log('定时器的this', this);
}, 3000);
</script>
</body>
1
# 定时器函数 this 指向的也是window

3.1.8、立即执行函数调用

1
2
3
4
5
6
7
8
9
<body>
<script>
// 函数的调用,this 的指向问题
// >> 立即执行函数
(function() {
console.log('立即函数的this', this);
})();
</script>
</body>
1
# 立即执行函数 this还是指向window

3.2、改变函数内部 this 指向

3.2.1、说明

​ JavaScript 为我们专门提供了一些函数方法来帮我们更优雅的处理函数内部 this 的指向问题,常用的有 bind()、call()、apply() 三种方法。

3.2.2、call方法

3.2.2.1、说明

1
2
3
4
5
6
7
8
9
10
11
# 语法
fun.call(thisArg, arg1, arg2, ...)
# 说明
1、thisArg:在 fun 函数运行时指定的 this 值
2、arg1,arg2:传递的其他参数
3、返回值就是函数的返回值,因为它就是调用函数
4、因此当我们想改变 this 指向,同时想调用这个函数的时候,可以使用 call,比如继承。

# 作用
1、可以调用函数
2、实现继承

3.2.2.2、案例1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<body>
<script>
// 改变函数内部 this 指向
// >> call方法

// 1、定义对象
var person = {
uname: 'HelloWorld'
};

// 2、定义函数
function fn(a, b) {
console.log(this); // {uname: 'HelloWorld'}
console.log(a + b);
}

fn.call(person, 10, 20);
</script>
</body>

3.2.2.3、案例2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<body>
<script>
// 改变函数内部 this 指向
// >> call方法

// 1、定义父类
function Father(uname, age) {
this.name = name;
this.age = age;
}
// 2、定义子类
function Child(uname, age, address) {
Father.call(this, uname, age);
this.address = address;
}

var zhangsan = new Child('张三', 20, '河南省');
console.log(zhangsan.uname);
console.log(zhangsan.age);
console.log(zhangsan.address);
</script>
</body>

3.2.3、apply 方法

3.2.3.1、说明

1
2
3
4
5
6
7
# 语法
fun.apply(thisArg, [argsArray])
# 说明
1. thisArg:在fun函数运行时指定的 this 值
2. argsArray:传递的值,必须包含在【数组】里面
3. 返回值就是函数的返回值,因为它就是调用函数
4. apply 主要跟数组有关系,比如使用 Math.max() 求数组的最大值

3.2.3.2、案例1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<body>
<script>
// 改变函数内部 this 指向
// >> apply方法

// 1、定义对象
var person = {
uname: 'HelloWorld'
}

// 2、定义函数
function fn(a, b) {
console.log(this);
console.log('a + b = ' + (a + b));
}

// 3、调用fn函数,fn函数需要的参数,使用apply调用的话,参数需要用数组的形式传递
fn.apply(person, [10, 20]);
</script>
</body>

3.2.3.3、案例2

apply 的主要应用 比如说我们可以利用 apply 借助于数学内置对象求数组最大值。

1
2
3
4
5
6
7
8
9
10
11
12
13
<body>
<script>
// 改变函数内部 this 指向
// >> apply方法

// 1、定义数组
var arrs = [10, 21, 18, 34, 23];

// 2、求最大值
var result = Math.max.apply(Math, arrs);
console.log(result);
</script>
</body>

3.2.4、bind 方法

3.2.4.1、说明

1
2
3
4
5
6
7
8
9
# 语法
fun.bind(thisArg, arg1, arg2, ...)
方法不会调用,但是也会改变函数this的指向。

# 说明
1. thisArg:在 fun 函数运行时指定的 this 值
2. arg1,arg2:传递的其他参数
3. 返回由指定的 this 值和初始化参数改造的原函数拷贝,也就是说返回的是一个函数,一个原函数的一个拷贝。
4. 因此当我们只是想改变 this 指向,并且不想调用这个函数的时候,可以使用 bind

3.2.4.2、案例1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<body>
<script>
// 改变函数内部 this 指向
// >> bind方法

// 1、定义对象
var person = {
uname: 'HelloWorld'
};

// 2、定义函数
function fn(a, b) {
console.log(this);
console.log("a + b = " + (a + b));
}

// 3、绑定,返回的是原函数改变this之后产生的新函数,新函数并不调用
var f = fn.bind(person, 10, 30);

// 4、调用
f();
</script>

</body>

3.2.4.3、案例2

​ 如果有的函数我们不需要立即调用,但是又想改变这个函数内部的this指向此时用bind。

​ 我们有一个按钮,当我们点击了之后,就禁用这个按钮,3秒钟之后开启这个按钮。

​ 错误的代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<body>
<button>注册</button>
<script>
// 改变函数内部 this 指向
// >> bind方法

// 1、获取button按钮
var btnObj = document.querySelector('button');

// 2、绑定点击事件
btnObj.onclick = function() {
// >> 2.1 将按钮禁用,this就是当前的button按钮
this.disabled = true;
// >> 2.2 开启定时器,3秒之后还原
setTimeout(function() {
// >> 2.3 不能用this,因为定时器中的this指的是window对象,不是按钮
this.disabled = false;
}, 3000);
}
</script>
</body>

​ 正确的代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<body>
<button>注册</button>
<script>
// 改变函数内部 this 指向
// >> bind方法

// 1、获取button按钮
var btnObj = document.querySelector('button');

// 2、绑定点击事件
btnObj.onclick = function() {
// >> 2.1 将按钮禁用,this就是当前的button按钮
this.disabled = true;
// >> 2.2 开启定时器,3秒之后还原
// >> 2.2.1 定义全局变量,保存this
var that = this;
setTimeout(function() {
// >> 2.3 用定义好的that变量
that.disabled = false;
}, 3000);
}
</script>
</body>

3.2.4.4、案例3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<body>
<button>注册</button>
<script>
// 改变函数内部 this 指向
// >> bind方法

// 1、获取button按钮
var btnObj = document.querySelector('button');

// 2、绑定点击事件
btnObj.onclick = function() {
// >> 2.1 将按钮禁用,this就是当前的button按钮
this.disabled = true;
// >> 2.2 开启定时器,3秒之后还原
setTimeout(function() {
this.disabled = false; // 使用bind之后,该函数内部的this也就指向了按钮
}.bind(this), 3000); // 此处的this指向的是button按钮
}
//通过使用bind,改变函数this指向,返回的函数并不会立即调用,而是过了3秒再去调用。使用bind正好
</script>
</body>

3.2.4.5、案例4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<body>
<button>注册</button>
<button>注册</button>
<button>注册</button>
<script>
// 改变函数内部 this 指向
// >> bind方法

// 1、获取button按钮
var btnObjs = document.querySelectorAll("button");

// 2、绑定点击事件
for (var i = 0; i < btnObjs.length; i++) {
btnObjs[i].onclick = function() {
// >> 将当前点击的按钮设置为禁用
this.disabled = true;
// >> 启用定时器,3秒启用
setTimeout(function() {
this.disabled = false;
}.bind(this), 3000);
}
}
</script>
</body>

3.3、call 、apply、bind 总结

1
2
3
4
5
6
7
8
9
10
# 相同点
都可以改变函数内部的this指向。
# 区别点
1. call 和 apply 会调用函数, 并且改变函数内部this指向.
2. call 和 apply 传递的参数不一样, call 传递参数 arg1, arg2..形式 apply 必须数组形式[arg]
3. bind 不会调用函数, 可以改变函数内部this指向.
# 主要应用场景
1. call 经常做继承.
2. apply 经常跟数组有关系. 比如借助于数学对象实现数组最大值最小值
3. bind 不调用函数,但是还想改变this指向. 比如改变定时器内部的this指向.

四、严格模式

4.1、什么是严格模式

​ JavaScript 除了提供正常模式外,还提供了严格模式(strict mode)。ES5 的严格模式是采用具有限制性 JavaScript 变体的一种方式,即在严格的条件下运行 JS 代码。

​ 严格模式在 IE10 以上版本的浏览器中才会被支持,旧版本浏览器中会被忽略。

​ 严格模式对正常的 JavaScript 语义做了一些更改:

  • 消除了 Javascript 语法的一些不合理、不严谨之处,减少了一些怪异行为。
  • 消除代码运行的一些不安全之处,保证代码运行的安全。
  • 提高编译器效率,增加运行速度。
  • 禁用了在 ECMAScript 的未来版本中可能会定义的一些语法,为未来新版本的 Javascript 做好铺垫。比如一些保留字如:class, enum, export, extends, import, super 不能做变量名。

4.2、开启严格模式

4.2.1、说明

​ 严格模式可以应用到整个脚本或个别函数中。因此在使用时,我们可以将严格模式分为脚本开启严格模式和为函数开启严格模式两种情况。

4.2.2、为脚本开启严格模式

​ 为整个脚本文件开启严格模式,需要在所有语句之前放一个特定语句“use strict”;(或‘use strict’;)。

1
2
3
4
5
6
7
8
<body>
<script>
'use strict';
console.log('这是严格模式...');

// 说明:为"use strict"加了引号,所以老版本的浏览器会把它当作一行普通字符串而忽略。
</script>
</body>

​ 有的 script 基本是严格模式,有的 script 脚本是正常模式,这样不利于文件合并,所以可以将整个脚本文件放在一个立即执行的匿名函数之中。这样独立创建一个作用域而不影响其他 script 脚本文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<body>
<script>
(function() {
'use strict';
var num = 20;
console.log(num);

// 该函数也是严格模式下的函数
function run() {

}
})();
</script>
</body>

4.2.3、为函数开启严格模式

​ 要给某个函数开启严格模式,需要把“use strict”; (或 ‘use strict’; ) 声明放在函数体所有语句之前。

​ 将 “use strict” 放在函数体的第一行,则整个函数以 “严格模式” 运行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<body>
<script>
// 此时只是给run函数开启严格模式
function run() {
'use strict';
console.log('该函数在严格模式下运行');
}

// 这个函数里面的还是按照普通模式执行
function sing(){

}
</script>
</body>

4.3、严格模式中的变化

4.3.1、说明

​ 严格模式对 Javascript 的语法和行为,都做了一些改变。

4.3.2、变量规定

  • 在正常模式中,如果一个变量没有声明就赋值,默认是全局变量。严格模式禁止这种用法,变量都必须先用var 命令声明,然后再使用。
  • 严禁删除已经声明变量。例如,delete x; 语法是错误的。
1
2
3
4
5
6
7
8
9
<body>
<script>
'use strict';
num = 20;
console.log(num); // 报错,Uncaught ReferenceError: num is not defined

// 总结: 变量名必须先声明再使用
</script>
</body>
1
2
3
4
5
6
7
8
9
10
<body>
<script>
'use strict';
var num = 20;
// 删除变量
delete num; // 报错:Delete of an unqualified identifier in strict mode

// 总结: 不能随意删除已经声明好的变量
</script>
</body>

4.3.3、严格模式下 this 指向问题

  • 以前在全局作用域函数中的 this 指向 window 对象。
  • ==严格模式下全局作用域中函数中的 this 是 undefined。==
  • 以前构造函数时不加 new也可以 调用,当普通函数,this 指向全局对象。但是在严格模式下,如果 构造函数不加new调用, this 指向的是undefined 如果给他赋值则 会报错
  • new 实例化的构造函数指向创建的对象实例。
  • 定时器 this 还是指向 window 。
  • 事件、对象还是指向调用者。
1
2
3
4
5
6
7
8
9
10
11
12
<body>
<script>
'use strict';

function fn() {
console.log(this); // undefined
}
fn();

// 总结:严格模式下全局作用域中函数中的 this 是 undefined。
</script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
<body>
<script>
'use strict';

function Star() {
this.uname = '张三';
}
Star(); // 报错,Cannot set properties of undefined (setting 'uname')

// 总结:严格模式下,构造函数如果不加new调用,则是普通函数调用,那么函数的this是undefined
</script>
</body>
1
2
3
4
5
6
7
8
9
10
11
<body>
<script>
'use strict';

setTimeout(function() {
console.log(this);
}, 3000);

// 总结:定时器 this 还是指向 window 。
</script>
</body>

4.3.4、函数变化

  • 函数不能有重名的参数。
  • 函数必须声明在顶层。新版本的 JavaScript 会引入“块级作用域”( ES6 中已引入)。为了与新版本接轨,不允许在非函数的代码块内声明函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
<body>
<script>
'use strict';

function sum(a, a) {
console.log(a + a);
}

sum(10, 20); // 报错,Duplicate parameter name not allowed in this context

// 总结:严格模式下函数里面的参数不允许有重名
</script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
<body>
<script>
'use strict';

if (true) {
function run() {
console.log('helloworld');
}
}
run(); // 报错,在严格模式下会报错,在普通模式下是不会报错的。原因是严格模式要和块级作用域接轨。
</script>
</body>