JavaScript函数
函数的返回值由什么确定
-
调用时
传入的参数params -
定义时
的环境env
举例:
let x = 'x'
let a = '1'
function f1(x1) {
return x + a
}
a = '3'
{
let a = '2'
f1('x') // 输出x3
}
这个例子说明a是定义时的a,而不是执行时的a
另一个更复杂的例子:
let x = 'x'
let a = '1'
function f1(c) {
c()
}
{
let a = '2'
function f2() {
console.log(x + a)
}
f1(f2) // 输出x2
}
在函数里面能访问外部的变量这不是天经地义的吗?
并不是,比如ruby就不允许你这样做!
def f1
a = 1
def f2
print a
end
f2()
end
f1() # 报错:NameError(undefined local variable or method 'a' for main:Object)
如果一定要ruby能在函数里访问外部的变量呢?
可以使用lamdba(相当于使用闭包)
def f1
a = 1 # 局部变量a
f2 = lamdba { print a } # 使用lambda打印a
f2.call()
end
f1() # 成功打印出1
闭包
如果在函数里面可以访问外面的变量,那么,这个函数 + 这些变量 = 闭包
一个很常见的考题
请问下面打印出什么
for (var i = 0; i < 6; i++) {
setTimeout(() => console.log(i)) // 箭头函数访问了 i
}
答案:输出6个6,因为6个函数在for循环结束后执行,并且6个函数 共用一个i
- 闭包的特点
- 能让一个函数维持住一个变量
- 但并不能维持这个变量的值
- 尤其是变量的值会变化的时候
- 对象是穷人的闭包
- 对象也可以用来维持住一个变量
- 如果一门语言不支持闭包,你可以用对象代理
// 对象维持变量
var obj = {
i: 0,
fn() {
console.log(this.i)
}
}
// 闭包维持变量
const handle = function () {
var i = 0
return function () {
console.log(i)
}
}
- 闭包是穷人的对象
- 如果一门语言不支持对象,你可以用闭包代理
function createPerson(age, name) {
return function (key) {
if (key === 'name') return anme
if (key === 'age') return age
}
}
var person = createPerson(18, 'flinn')
person('name') // 'flinn'
person('age') // 18
声明一个函数
4种方式
const f1 = new Function('x', 'y', 'return x + y')
function f2(x, y) {return x + y}
const f3 = function (x, y) {return x + y}
const f4 = (x, y) => x + y
其中,f1,f2,f3是ES6之前的语法,支持this/arguments/new
而f4是ES6新出的语法,不支持this/arguments/new
这里的不支持是什么意思?箭头函数里写this不是不报错吗?
因为箭头函数的this和调用时无关,只和定义时的环境有关
非箭头函数的this
网传死记方法
- this是上下文啦
- 全局环境执行函数时,this是全局对象
- 调用对象的方法时,this是该对象
- 函数里调用函数时,this是全局对象
- 箭头函数里的this,不看调用,看定义
- 还有人说箭头函数里的this指向外面的this
- 用new调用函数时,this是新增对象
- 可用call/apply/bind指定this
那么普通函数的this是参数还是环境?
答:this是参数(隐式参数)
this的确定
- 显式this
fn.call(asThis, 1, 2)
fn.bind(asThis, 1, 2)()
obj.method.call(obj, 'hi')
- 隐式this
fn(1, 2) // fn.call(undefined, 1, 2)
obj.method('hi') // obj.method.call(obj, 'hi')
array[0]('hi') // array[0].call(array, 'hi')
测试一下
button.onclick = function(e) {
console.log(this) // 答案是:无法确定
}
button.onclick() // 这样调用this是button
var f = button.onclick
f() // 这样调用this是window
const vm = new Vue({
data: {
message: 'hi'
},
methods: {
sayHi() {
console.log(this.message) // this是什么?答案是不确定,和上面同理,要看调用的时候是啥情况
}
}
})
面试题
// 用let声明的length不会挂载到window上,而window.length默认指的是iframe的个数
let length = 10
function fn() {console.log(this.length)}
let obj = {
length: 5,
method(fn) {
fn()
arguments[0]() // 相当于arguments[0].call(arguments),而arguments.length为实参的长度
}
}
obj.method(fn, 1) // 输出什么,有陷阱。 答案是:不确定(window.length不确定) 2