函数进阶
函数的定义和调用(函数也是对象)
- 函数声明方式
function
关键字(命名函数)- 函数表达式(匿名函数)
new Function
Function
里面的参数必须是字符串格式的- 执行效率低,不方便书写,因此较少使用
- 所有的函数都是
Function
的实例对象
new Function('参数1', '参数2', '函数体')
let fun = new Function(n, m, 'console.log(333)');
fun(1, 2);
函数的调用方式
-
普通函数
function fn(){} fn(); fn.call();
-
对象的方法
let fun = { say: function() { console.log('呵呵') } }; fun.say();
-
构造函数
function Star() {}; new Star();
-
绑定事件函数
btn.onclick = function() {}; //=>点击按钮就可以调用这个函数
-
定时器函数
srtInterval(function() {}, 1000); //=>这个函数是定时器自动一秒钟调用一次
-
自执行函数
(function() { console.log('呵呵'); })(); // 自执行函数自动调用
函数内部this
的指向
函数内的
this
指向,是在调用函数的时候确定的,调用方式的不同决定了this
指向的不同,一般指向函数的调用者(实例对象)
调用方式 | this 指向 |
---|---|
普通函数调用 | window |
构造函数调用 | 实例对象(原型对象里面的this 也指向实例对象) |
对象的方法调用 | 所属对象 |
事件绑定函数调用 | 绑定事件对象(函数的调用者) |
定时器函数调用 | window |
自执行函数(不需要手动调用) | window |
改变函数内部的this
指向
常用的处理函数内部
this
指向问题的方法:call()
、bind()
、apply()
call()
方法
call()
方法调用一个对象,简单的理解为调用函数的方式,但是它可以改变函数的this
指向
fun.call(thisAag, arg1, arg2, ...);
// 第一个值为this指向,剩下的为参数值
call()
(1)可以调用函数;(2)第二个可以改变函数内的this指向;(3)主要作用是可以实现继承
// 利用call实现继承
function Father(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
function Son(name, age, sex) {
Father.call(this, name, age, sex);
}
let son = new Son('赵四', 18, '男');
apply()
方法
apply()
方法调用一个函数,简单的理解为调用函数的方式,但它可以改变函数的this
指向
fun.apply(thisArg, [argsArray])
thisArg
:在fun函数运行的是否指定的this值argsArray
:传递的参数,必须包含在数组里面- 返回值就是函数的返回值,因为它是调用函数
apply()
调用函数;改变函数内部的this
指向;但是它的参数必须是数组形式(伪数组)
// 求数组中的最大值、最小值
var arr = [1, 3, 57, 99, 6];
let max = Math.max.apply(Math, arr);
let min = Math.min.apply(Math, arr);
console.log(max, min);
bind()
方法
bind()
方法不会调用函数,但能改变函数内部this
指向,返回的是原函数改变this
之后产生的新函数
fun.bind(thisArg, arg1, arg2, ...)
thisArg
:在fun函数运行时指定的this值arg1,arg2
:传递的参数- 返回由指定的
this
值和初始化参数改造的原函数拷贝
let ary = {
name: 'andy'
};
function fn(a, b) {
console.log(this);
console.log(a + b);
};
let f = fn.bind(ary, 1, 2);
f();
实际开发中,有的函数我们不需要立即调用,但是又想改变这个函数内部的
this
指向,此时用bind()
====================
call()
apply()
bind()
方法总结
- 相同点:都可以改变函数内部的
this
指向- 区别:
call()
和apply()
会调用函数,并且改变函数内部this
指向call()
和apply()
传递的参数不一样,call()
传递参数ary1,ary2...
形式,apply()
传递参数必须是数组形式[arg]
bind()
不会调用函数,可以改变函数内部的this
指向
主要应用场景call()
经常做继承apply()
经常跟数组有关系,比如借助于数学对象实现数组最大值最小值bind()
不调用函数,但是还想改变函数this
指向,比如改变定时器内部的this
指向
严格模式
JavaScript除了提供正常模式外,还提供了严格模式(strict mode),ES5的严格模式是采用具有限制性JavaScript变体的一种方式,即在严格的条件下运行JS代码
严格模式在IE10以上版本的浏览器中才会被支持,旧版本浏览器中会被忽略
严格模式对正常的JavaScript语义做了一些更改:
- 消除了JavaScript语法的一些不合理、不严谨之处,减少了一些怪异行为
- 消除代码运行的一些不安全之处,保证代码运行安全
- 提高编译器效率,增加运行速度
- 禁用了ECMAScript的未来版本中可能会定义的一些语法,为未来新版本的JavaScript做好铺垫。比如一些保留字:
class
,enum
,export
,extends
,import
,super
不能做变量名
开启严格模式
严格模式可以应用到整个脚本或个别函数中,因此在使用时,我们可以将严格模式分为为脚本开启严格模式和为函数开启严格模式两种情况
- 为脚本开启严格模式
为整个脚本文件开启严格模式,需要在所有语句之前放一个特定语句
"use strict"
;(或'use strict'
;)
- 为函数开启严格模式
要给某个函数开启严格模式,需要把
"use strict"
;(或'use strict'
;)声明放在函数体所有语句之前
function fn() {
'use strict';
// 下面的代码按照严格模式来执行
}
function fun() {
// 里面还是按照普通模式执行
}
严格模式中的变化
严格模式对JavaScript的语法和行为都做了一些改变
- 变量规定:
- 在正常模式中,如果有一个变量没有声明就赋值,默认是全局变量.严格模式下禁止这种用法,变量必须声明,然后在使用
- 严禁删除已经声明的变量,例如:
delete x;
语法是错误的- 严格模式下
this
指向问题
- 以前在全局作用域函数中的
this
指向window
对象- 严格模式下全局作用域中的函数中的
this
是undefined
- 以前构造函数不加new也可以调用,当作普通函数执行,
this
指向全局对象window
- 严格模式下,如果构造函数不加
new
调用会报错,因为this
指向undefined
,如果加new
调用,this
指向创建的实例对象- 定时器里面的this还是指向window
- 事件对象的
this
还是指向调用者- 函数变化
- 严格模式下函数里面的参数不允许有重名
- 严格模式下禁止不在脚本或者函数层面上的函数声明
"use strict"
if(true){
function fn() {} // 语法错误
fn();
}
for(let i = 0;i < 5; i++){
function fun() {} // 语法错误
fun();
}
function f() { // 合法
function f1() {} // 同样合法
}
高阶函数(把函数作为参数和返回值的传递)
高阶函数就是对其它函数进行操作的函数,它接收函数作为参数,或将函数作为返回值输出
此时
fn
就是一个高阶函数
函数也是一种引用数据类型,同样可以作为参数,传递给另外一个函数使用,最典型的就是回调函数
function fn(a, b, callback) {
console.log(a + b);
callback && callback();
}
fn(1, 2, function() {
console.log('最后调用的');
});
闭包
变量作用域
变量根据作用域的不同分为两种:全局变量和局部变量
函数内部可以使用全局变量,函数外部不可以使用局部变量
当函数执行完毕,其作用域内的局部变量就会被销毁
闭包定义
闭包(closure)指有权访问另一个函数作用域中变量的函数(简单的理解就是:一个作用域可以访问另一个函数内部的局部变量)
闭包的作用:延伸了变量的作用范围
(后面的改天再看,作业都快写不完了)