1. 函数的定义和调用
1.1 函数的定义方式
function fn() { };
var fun = function() { };
- 利用 new Function ,了解即可,因为效率低,也不方便书写,较少使用
var fn = new Function('参数1','参数2'..., '函数体');
1.2 函数的调用方式
function fn () {
console.log('hello');}
fn();
fn.call();
var hi = {
sayHi: function(){
console.log('hello');
}
}
hi.sayHi();
function Star() { };
new Star();
btn.onclick = function() { };
setInterval(function() { }, 1000);
(function() {})();
2. this
2.1 函数内this的指向
这些this的指向,是当我们调用函数的时候确定的。 调用方式的不同决定了this的指向不同
一般指向我们的调用者:
调用方式 | this指向 |
---|
普通函数调用 | window | 对象方法调用 | 该方法所属对象 | 构造函数调用 | 实例对象 原型对象里面的方法也指向实例对象 | 事件绑定方法 | 绑定事件对象 | 定时器函数 | window | 立即执行函数 | window |
2.2 改变函数内this的指向
JS为我们专门提供了一些函数方法更优雅的处理函数内部this的指向问题,常用的又 call()、 apply()、 bind() 三种方法
- call()方法
调用一个对象,简单理解为调用函数的方式,但是它可以改变函数的this指向 主要应用场景:call经常做继承。
fn.call(thisArg, arg1,arg2,...)
- apply()方法
调用一个对象,简单理解为调用函数的方式,但是它可以改变函数的this指向 主要应用场景:例如借助于数学内置对象求最大值( 经常跟数组有关系)
fn.apply(thisArg, [argsArray])
var arr = [1,77,3,5,2];
Math.max.apply(Math,arr);
- bind()方法—重点记住
不会调用原来的函数,但可以改变函数的this指向 典型应用: 如果有的函数我们不需要立即调用,但是又想改变这个函数内部的this指向,此时用blind()方法,比如改变定时器内部this指向,这个方法用的较多
fn.bind(thisArg, arg1,arg2,...)
var btn = document.querySelector('button');
btn.onclick = function(){
this.disabled = true;
setTimeout(function(){
}.bind(this),3000)
}
3. 严格模式
JS除了提供正常模式外,还提供了严格模式(strict mode),即在严格条件下运行JS代码。 严格模式对正常的JS语义做了一些更改:
- 消除了JS语法的一些不合理、不严谨之外,减少了一些怪异行为
- 消除代码运行的一些不安全之处,保证代码运行的安全
- 提高编译器效率,增加运行速度
- 禁用了在ECMAScript的未来版本中可能会定义的一些语法,为未来新版本的JavaScript做好铺垫。比如一些保留字: class、enum、 export、 extends、 import、 super不能做变量名
3.1 开启严格模式
严格模式可以应用到整个脚本或者个别函数中。因此在使用时,我们可以讲严格模式分为脚本开启严格模式 和为函数开启严格模式 两种情况。
有的script脚本文件是严格模式,有的是正常模式,这样不利于文件合并,所以可以将整个脚本文件放在一个立即执行的匿名函数之中,这样独立创建一个作用域而不影响其他script脚本文件。
'use strict';
<script>
(function () {
'use strict';
var num = 10;
function fn() { }
})();
</script>
function fn() {
'use strict';
}
function fn() {
}
3.2 严格模式中的变化
严格模式对js的语法和行为,都做了一些改变
变量规定
- 在正常模式中,如果一个变量没有声明就赋值,默认是全局变量; 严格模式下禁止这种用法,变量必须先声明,再使用
- 严禁删除已经声明变量,例如 delet a; 语法报错
严格模式下this指向问题
- 以前在全局作用域函数中的this指向window对象
- 严格模式下全局作用域中函数中的this是
undefined - 以前构造函数不加new也可以,会被当成普通函数,因为this指向window全局对象
- 严格模式下,如果构造函数不加new调用,this会报错,因为是undefined
- new实例化的构造函数指向创建的对象实例
- 严格模式下,定时器this还是指向 window
- 事件、对象还是指向调用者
函数变化
- 函数不能有重名的参数
- 函数必须声明在顶层,ES6引入块级作用域,不允许在非函数的代码块声明函数,例如,if 、for里面不要直接写函数
- 更多严格模式要求参考:https://developer.mozilla.org/zh-Cn/docs/Web/JavaScript/Reference/Strict_mode
4. 高阶函数
高阶函数是对其他函数进行操作的函数,它接收函数作为参数 或者将函数作为返回值输出 函数也是一种数据类型,同样可以作为参数,传递给另外一个参数使用,最典型的就是作为回调函数
<script>
function fn(callback){
callback && callback();
}
fn(function(){alert('hi')})
</script>
<script>
function fn(){
return function(){}
}
fn();
</script>
5. 闭包
5.1 变量作用域
变量根据作用域的不同分为两种:全局变量和局部变量
- 函数内部可以使用全局变量
- 函数外部不可以使用局部变量
- 当函数执行完毕,本作用域内的局部变量会销毁
5.2 什么是闭包
闭包(closure) 指有权 访问另一个函数作用域中变量 的 函数 简单理解就是,一个作用域可以访问另外一个函数内部的局部变量
闭包的作用: 延伸了变量的作用范围
案例1: 循环注册点击事件
<script>
var lis = document.querySelectorAll('.nav li');
for (var i = 0; i < lis.length; i++) {
lis[i].index = i;
lis[i].onclick = function () {
console.log(i);
console.log(this.index);
}
}
for (var i = 0; i < lis.length; i++) {
(function (i) {
lis[i].onclick = function () {
console.log(i);
}
})(i);
}
</script>
案例2: 3秒钟之后打印li内容
<script>
var lis = document.querySelectorAll('.nav li');
for (var i = 0; i < lis.length; i++) {
(function (i) {
setTimeout(function () {
console.log(lis[i].innerHTML)
}, 3000)
})(i);
}
</script>
案例3: 计算打车价格
<script>
var car = (function () {
var start = 13;
var total = 0;
return {
price: function (n) {
if (n <= 3) {
total = start;
} else {
total = (n - 3) * 5 + start;
}
return total;
},
yongdu: function (flag) {
return flag ? total + 10 : total;
}
}
})();
console.log(car.price(3));
console.log(car.yongdu(true));
</script>
思考题:立即执行函数的 this 指向 window
6. 递归
6.1 什么是递归
如果一个函数在内部可以调用其本身 ,那么这个函数就是递归函数 简单理解:函数内部自己调用自己,这个函数就是递归函数 由于递归很容易发生 “栈溢出” 错误(stack overflow),所以必须加退出条件 return
6.2 利用递归
利用递归函数求1-n阶乘 1 * 2 * 3 * 4 …n
function fn(n) {
if (n == 1) {
return 1;
}
return n * fn(n - 1);
}
console.log(fn(3));
console.log(fn(4));
6.3 浅拷贝和深拷贝
浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用
Object.assign(target,...sources)
深拷贝拷贝多层,每一级别的数据都会拷贝
|