函数的基本用法
arguments
arguments
是在函数作用域内存在的一个类数组对象,存储函数的参数等属性。
function foo() {
console.log(arguments)
for (var i = 0; i < arguments.length + 2; i++) {
console.log(arguments[i])
}
}
foo(1, 2, "哈哈哈", {}, [1, 2]) //
arguments
参数只有在函数作用域里面才生效,它是一个对象,但是和数组一样可以通过[0][1]
的方式依顺序访问函数的参数,并且具有一个length
属性,访问的是函数的参数的个数。arguments
里面还有属性callee
,指的是函数本身,可以在函数内部调用函数自己(不推荐使用)
var sum = function(num) {
if (num > 0) {
return arguments.callee(num - 1) + num
}
return num
}
sum(100) //5050
这里不知道函数名字(匿名函数),但是还是依旧可以调用自身。
箭头函数没有arguments
,用剩余参数...rest
代替
剩余参数
现在有一个需求:需要一个函数,传入不定长参数,第一个参数是名字,第二个以后都是数字,函数返回名字和第二个以后参数的总和
function add(name, ...rest) {
let sum = 0
for (var i = 0; i < rest.length; i++) {
sum += rest[i]
}
return name + sum
}
add("蛤", -1, -1, -1, -1, -1, -1, -1) //"蛤-7"
将多余的实参传入rest
数组中,如果没有多余的则rest
数组为空
默认参数
function Person(name = "未知", sex = "未知", age = 18) {
var o = {
name: name,
age: age,
sex: sex,
}
return o
}
Person("紫", "妈") //{ name: '紫', age: 18, sex: '妈' }
用于应对不定参数或者参数残缺的接口。
arguments.callee
callee属性可以引用该函数的函数体内当前正在执行的函数
function foo() {
console.log(arguments.callee.name)
}
foo() //foo
this
this
是"这个"的意思,this 指向调用函数的主体对象。
console.log(this) //window,浏览器对象
//这意味着他和window.console.log等价
window.console.log === console.log //true,浏览器对象调用console.log
在全局中this
指向window
var name = "window name"
//全局的name是当window存在都不会消失,跳转也不会
var o = {
name: "this object",
getThis: function() {
console.log(this === o, this.name)
},
}
o.getThis() //true "this object"
此时getThis
函数是o
调用的,所以函数内部的this
指向o
但是
var o = {
name: "this object",
getThis: function() {
console.log(this === o, this.name)
},
}
var name = "window name"
var fn = o.getThis
fn() //false "window name"
为什么此时就变成了false
了呢。因为fn
获得是一个函数。现在调用 fn()则变成了window.fn()
,此时的this
指向了window
,调用函数的主体是 window,this.name===window.name
,变成了"window name"。
在箭头函数里的this
var name = "window name"
var o = {
name: "this object",
fn: function() {
console.log(this === o)
//let that = this;
return () => {
console.log(this === that, this.name)
}
},
}
o.fn() //true
let foo = o.fn()
foo() //true "this object"
//此时o.fn()是箭头函数()=>{},this指向声明阶段的上下文,也就是o
箭头函数中使用 this
箭头函数没有 this,this 指向是外部上下文的 this(无法改变)
在普通函数中 this 的绑定是在执行位置绑定
在箭头函数里面的 this 在声明阶段硬绑定到上下文中。
var name = "window name"
var o = {
name: "this object",
fn: function() {
console.log(this === o)
let that = this
return function() {
console.log(this === that, this.name)
}
},
}
o.fn()() //true false "window name"
//此时o.fn()是function,执行时this指向window
- 作用
绑定事件
let leftBtn = document.querySelector(".left")
leftBtn.onclick = function() {
this.style.backgroundColor = "red" //点击事件触发的主体是leftBtn
}
循环绑定
let lilist = document.querySelectorAll(".list li")
for (var i = 0; i < lilist.length; i++) {
lilist[i].onclick = function() {
this.innerHTML = "666"
}
}
call
apply
bind
函数的 this 的指向是可以修改的。我们需要用到某些特殊的原型的方法的时候会用到 call,apply 还有 bind 强行修改。
call/apply
var name = "window name"
var o1 = {
name: "object name",
getThis: function() {
console.log(this.name)
},
}
var o2 = {
name: "another name",
}
o1.getThis() //"object name"
var fn = o1.getThis
fn() //"window name"
fn.call(o2) //"another name"
fn.apply(o2) //"another name"
fn.call(o1) //"object name"
此时call/apply
都会使函数执行,相当于强行修改了调用函数的主体对象,在你想要的对象上借用了别的方法。方法的一处书写,到处使用。
var o = {
"0": "123",
"1": "456",
"2": "789",
"length": 3,
}
o.forEach(function(item) {
console.log(item);
}); //此处会报错,因为对象o没有forEach方法,这是数组的方法
//于是
[].forEach.call(o, function(i) {
console.log(i)
}) //打印"123" "456" "789"
Array.prototype.forEvery = function(cb) { //不完全方法
for (var i = 0; i < this.length; i++) {
cb(this[i]);
}
} //自己定义了一个方法
[1, 2, 3, 4].forEvery(function(i) {
console.log(i);
}) //1 2 3 4
[].forEvery.call(o, function(i) {
console.log(i)
}) //"123" "456" "789"
call
,apply
的唯一区别
fn.call(this, x1, x2, x3, x4) //函数原本参数是一个一个传进去的
fn.apply(this, [x1, x2, x3, x4]) //函数原本参数放到数组中传进去
bind
bind
方法同样也可以修改函数执行的主体对象,但是bind
是硬绑定,是在执行的时候返回一个绑定完成之后的函数,可以再任意地方执行
var name = "window name"
var o1 = {
name: "object name",
getThis: function() {
console.log(this.name)
},
}
var o2 = {
name: "another name",
}
var sayName = o1.getThis.bind(o2) //返回一个绑定好的函数,啥都不返回
sayName() //"another name"
sayName.call(o1) //"another name"已经改不了了