目录
函数的定义和调用
函数的定义方式
1.自定义函数(命名函数)
function fn() {}
2.函数表达式(匿名函数)
var fn = function() {}
3.new Function()
var fn = new Function('参数1','参数2','函数体')
/*var fn = new Function('a','b','console.log(a+b)')
fn()*/
Function里面参数都必须是字符串格式
第三种方式执行效率低,也不方便书写,因此较少使用
所有函数都是Function 的实例(对象)
函数也属于对象
函数的调用方式
1.普通函数
function fn(){}
//调用: fn() / fn.call()
2.对象的方法
var obj = {
sing:function(){}
}
//调用: obj.sing()
3.构造函数
function Str(){}
//调用: new Str()
4.绑定事件函数
btn.onclick=function(){} //触发点击事件就调用函数
5.定时器函数
setInterval(function(){},1000) //定时器自动调用
6.立即执行函数
(function(){})()
this
谁调用就指向谁
调用方式 | this指向 |
---|---|
普通函数 | window |
对象的方法 | 该方法所属对象 |
构造函数 | 实例对象,原型对象中的方法也指向实例对象 |
绑定事件函数 | 绑定事件对象 |
定时器函数 | window |
立即执行函数 | window |
改变函数内部this指向
1.call()方法
fun.call(thisArg, arg1, arg2, ...)
/*
thisArg:在 fun 函数运行时指定的 this 值
arg1,arg2:传递的其他参数
返回值就是函数的返回值,因为它就是调用函数
因此当我们想改变 this 指向,同时想调用这个函数的时候,可以使用 call,比如继承
*/
// 1.改变this指向
var user = {
name: '张三',
age: 18
}
function fn() {
console.log(this);
}
fn() // this指向window
fn.call(user) //this指向对象{name: '张三', age: 18}
// 2.实现继承
function Father(uname, age) {
this.uname = uname
this.age = age
}
function Son(uname, age) {
Father.call(this, uname, age)
}
var son = new Son('andy', 20)
console.log(son)
2.apply()方法
fun.apply(thisArg, [argsArray])
/*
thisArg:在fun函数运行时指定的 this 值
argsArray:传递的值,必须包含在数组里面
返回值就是函数的返回值,因为它就是调用函数
因此 apply 主要跟数组有关系,比如使用 Math.max() 求数组的最大值
*/
// 1.改变this指向
var user = {
name: '张三',
age: 18
}
function fn() {
console.log(this);
}
fn() // this指向window
fn.apply(user) //this指向对象{name: '张三', age: 18}
console.log('----------')
// 2.apply的主要应用,利用apply借助数学内置对象求最大值/最小值
function obj(data) {
console.log(data)
}
var arr = [2, 65, 37, 94, 11]
obj.apply(user, arr)
var max = Math.max.apply(Math, arr)
var min = Math.min.apply(Math, arr)
console.log(max, min)
3.bind()方法
fun.bind(thisArg, arg1, arg2, ...)
/*
bind() 方法不会调用函数。但是能改变函数内部this 指向
thisArg:在 fun 函数运行时指定的 this 值
arg1,arg2:传递的其他参数
返回由指定的 this 值和初始化参数改造的原函数拷贝
因此当我们只是想改变 this 指向,并且不想调用这个函数的时候,可以使用 bind
*/
// 1.改变this指向
var user = {
name: '张三',
age: 18
}
function fn() {
console.log(this);
}
fn() // this指向window
fn.bind(user) // bind方法不会调用函数,但可以改变函数内this指向
var Fn = fn.bind(user) //返回的是原函数改变this指向之后产生的新函数
Fn() // 调用函数,打印出this指向
// 2.bind方法应用
var btns = document.querySelectorAll('button')
for (var i = 0; i < btns.length; i++) {
btns[i].addEventListener('click', function () {
this.disabled = true
setTimeout(function () {
this.disabled = false
}.bind(this), 2000) //因为bind(this)在定时器函数外,所以这个this指向的是调用者btn,定时器中的this指向的是window,即可以将this指向从window改为btn
//这个函数我们并不需要立即调用,而是在一段时间后由定时器来调用,使用call,apply方法固然可以改变函数this指向,但他们会立即调用函数
})
}
总结比较
相同点: 都可以改变函数内部的this指向.
区别点:
1.call 和 apply 会调用函数, 并且改变函数内部this指向.
2.call 和 apply传递的参数不一样,call 传递参数 aru1,aru2..形式 apply 必须数组形式[arg]
3.bind 不会调用函数, 可以改变函数内部this指向.
主要应用场景:
1.call 经常做继承.
2.apply经常跟数组有关系. 比如借助于数学对象实现数组最大值最小值
3.bind 不调用函数,但是还想改变this指向. 比如改变定时器内部的this指向.
严格模式
JavaScript 除了提供正常模式外,还提供了严格模式(strict mode)。ES5 的严格模式是采用具有限制性 JavaScript变体的一种方式,即在严格的条件下运行 JS 代码。
为脚本开启严格模式
//为整个脚本文件开启严格模式,需要在所有语句之前放一个特定语句“use strict”;(或‘use strict’;)
<script>
"use strict";
......
</script>
//有的 script 基本是严格模式,有的 script 脚本是正常模式,这样不利于文件合并,所以可以将整个脚本文件放在一个立即执行的匿名函数之中。这样独立创建一个作用域而不影响其他 script 脚本文件。
<script>
(function (){
"use strict";
......
})();
</script>
为函数开启严格模式
//要给某个函数开启严格模式,需要把“use strict”; (或 'use strict'; ) 声明放在函数体所有语句之前
function fn(){
"use strict";
......
}
严格模式的规范
1.变量规定
①在正常模式中,如果一个变量没有声明就赋值,默认是全局变量。严格模式禁止这种用法,变量都必须先用var 命令声明,然后再使用。
②严禁删除已经声明变量。例如,deletex; 语法是错误的。
2.this 指向问题
①以前在全局作用域函数中的this 指向 window 对象。
②严格模式下全局作用域中函数中的 this 是 undefined。
③以前构造函数时不加 new也可以 调用,当普通函数,this指向全局对象
④严格模式下,如果构造函数不加new调用, this 指向的是undefined如果给他赋值则会报错
⑤new 实例化的构造函数指向创建的对象实例。
⑥定时器 this 还是指向 window。
⑦事件、对象还是指向调用者。
3.函数变化
①函数不能有重名的参数。
②函数必须声明在顶层.新版本的 JavaScript会引入“块级作用域”( ES6 中已引入)。为了与新版本接轨,不允许在非函数的代码块内声明函数。
更多严格模式要求参考:严格模式 - JavaScript | MDN
高阶函数
高阶函数是对其他函数进行操作的函数,它接收函数作为参数或将函数作为返回值输出。
//1.函数作为参数传递
<script>
function fn(callback){
callback&&callback();
}
fn(function(){alert('hi')}
</script>
//2.函数作为返回值
<script>
function fn(){
return function() {}
}
fn();
</script>
闭包
闭包(closure)指有权访问另一个函数作用域中变量的函数
作用:延伸变量的作用范围
递归
如果一个函数在内部可以调用其本身,那么这个函数就是递归函数。
浅拷贝
<script>
var obj = {
name: 'andy',
age: 18,
msg: {
sex: 'girl',
hobby: 'sport'
}
}
var other = {}
for (var k in obj) { //遍历(循环)obj
console.log(k) // k是属性名(obj里面的k -- k in obj),
console.log(obj[k]) // obj[k]是属性值,相当于obj.k
console.log('----------')
/*
打印结果:
name
andy
---------- 第一次遍历打印出属性名name和属性值andy
age
18
---------- 第二次遍历打印出属性名age和属性值18
msg
{sex: 'girl', hobby: 'sport'}
---------- 第三次遍历打印出属性名msg和属性值{sex: 'girl', hobby: 'sport'}
*/
other[k] = obj[k] //意味着每次遍历给other添加一个属性名,并把对应的属性值从原对象中拷贝给新的对象
}
console.log(other)
// 但是浅拷贝在拷贝复杂数类型时,是把原对象(obj)这个复杂数据类型(msg)的地址给新对象(other),此时这俩个对象中的复杂数据类型(mag)共用一个开辟的空间(地址),当我们去修改新对象(other)中mag中的某一个属性或属性值时,原对象中的数据也会跟着改变
</script>
//es6新增语法 浅拷贝语法糖
Object.assign(other, obj) //把obj拷贝给other 代替for in
深拷贝
<script>
var obj = {
name: 'andy',
age: 18,
color: ['pink', 'red'],
msg: {
sex: 'girl',
hobby: 'sport'
}
}
var other = {}
function deepCopy(newobj, oldobj) {
for (var k in oldobj) {
// 数组
if (oldobj[k] instanceof Array) {
newobj[k] = []
deepCopy(newobj[k], oldobj[k])
}
else if (oldobj[k] instanceof Object) {
newobj[k] = {}
deepCopy(newobj[k], oldobj[k])
}
else {
newobj[k] = oldobj[k]
}
}
}
deepCopy(other, obj)
console.log(other)
other.msg.hobby = 'play game' //改变新的对象中的数据不影响原来对象的数据,因为他们复杂数据类型都是各自有一个开辟的空间,地址也不相同,因此改变任何一个的数据都不会影响另一个
console.log(obj)
</script>