看到代碼就開始答題,必錯(cuò)!!!聲明提前?。。∽兞刻嵘。。?/p>
一、函數(shù)的 5 種聲明(重點(diǎn)匿名,具名,箭頭)
- 1、具名函數(shù)
function f(x,y){
return x+y
}
f.name // 'f'
var f5 = function f4(){ //f4的作用域是這個(gè)函數(shù)本身
}
console.log(f);//可訪問
- 2、匿名函數(shù)
var f
f = function(x,y){ //f引用函數(shù) f記錄的是函數(shù)的地址,不是函數(shù)本身
return x+y
}
var f2 = f;//復(fù)制的函數(shù)地址,同時(shí)指向函數(shù)
f.name // 'f' ,他是匿名函數(shù),但是他有name
f2.name // 'f'
- 3、具名函數(shù)賦值
var f
f = function f2(x,y){ return x+y }
f.name // 'f2'
console.log(f2) // undefined
- 4、window.Function
var f = new Function('x','y','return x+y')
f.name // "anonymous"
- 5、箭頭函數(shù)
var fn6 = i=>i+1 //一個(gè)參數(shù)
var fn7 = (i,j)=>i+j //兩個(gè)參數(shù)
var fn8 = (i,j)=>{console.log(1);return i+j;}//執(zhí)行多條內(nèi)容,用{括起來}
var f = (x,y) => {
return x+y
}
var sum = (x,y) => x+y
var n2 = n => n*n
這幾種方法大體相同,只是this不同
二、如何調(diào)用函數(shù)
f.call(asThis, input1,input2)
其中 asThis 會(huì)被當(dāng)做 this,[input1,input2] 會(huì)被當(dāng)做 arguments
禁止使用 f(input1, input2)
三、作用域
- 按照語法樹,就近原則
- 我們只能確定變量是哪個(gè)變量,但是不能確定變量的值
拿到代碼直接做——必然會(huì)錯(cuò)。請(qǐng)先提升聲明!!!(只提聲明,不帶值)
詞法作用域(靜態(tài)作用域)練習(xí):
var global1 = 1
function fn1(param1){
var local1 = 'local1'
var local2 = 'local2')
function fn2(param2){
var local2 = 'inner local2'
console.log(local1)
console.log(local2)
}
function fn3(){
var local2 = 'fn3 local2'
fn2(local2)
}
}

詞法樹只用來分析語義,和值沒有關(guān)系
面試題1
var aa = 1;
function f(){
console.log(aa)
}
aa = 2;
f.call()
var a = 1
function f1(){
alert(a) // 是多少
var a = 2
}
f1.call()
答案:
var a = 1
function f1(){
var a //聲明提前
alert(a) // undefined
a = 2
}
f1.call()
面試題2
var a = 1
function f1(){
var a = 2
f2.call()
}
function f2(){
console.log(a) // 是多少
}
f1.call()
答案:
var a = 1
function f1(){
var a = 2 //a=2只在當(dāng)前作用域(f1)內(nèi)有效
f2.call()
}
function f2(){
console.log(a) //1,拿的是全局a = 1
}
f1.call()
面試題3
var liTags = document.querySelectorAll('li')
for(var i = 0; i<liTags.length; i++){
liTags[i].onclick = function(){
console.log(i) // 點(diǎn)擊第3個(gè) li 時(shí),打印 2 還是打印 6?
}
}
深入閱讀:
四、什么是 call stack
stack堆:先進(jìn)后出
隊(duì)列:先進(jìn)先出
瀏覽器不是先運(yùn)行代碼,而是先把申明提到前面

五、this 和 arguments
-
重要:this 就是 call 第第一個(gè)參數(shù)!call 的其他參數(shù)統(tǒng)稱為 arguments
-
this是隱藏的第一個(gè)參數(shù),且必須是對(duì)象
function f(){
console.log(this)
console.log(arguments)
}
f.call() // window
f.call({name:'frank'}) // {name: 'frank'}, []
f.call({name:'frank'},1) // {name: 'frank'}, [1]
f.call({name:'frank'},1,2) // {name: 'frank'}, [1,2]
不傳this,默認(rèn)window
不傳arguments,默認(rèn)是空數(shù)組[]
為什么用call,不用f()直接調(diào),用f()是閹割版的f.call(),就不容易分辨出this代表是什么
function f(){
'use strict'
console.log(this)
console.log(arguments)
return undefined
}
f.call();//需傳入this和arguments([]),如果沒有傳,window不給this,就默認(rèn)是undefined,也就是window(小寫window,google瀏覽器打印出來是Window,所以運(yùn)行結(jié)果是window,[]
f.call(1,2,3) // this 為 1,arguments 為 [2,3]
-
this為什么必須是對(duì)象
因?yàn)?this 就是函數(shù)與對(duì)象之間的羈絆
var person = {
name: 'frank',
sayHi: function(person){
console.log('Hi, I am' + person.name)
},
sayBye: function(person){
console.log('Bye, I am' + person.name)
},
say: function(person, word){
console.log(word + ', I am' + person.name)
}
}
person.sayHi(person)
person.sayBye(person)
person.say(person, 'How are you')
// 能不能變成
person.sayHi()
person.sayBye()
person.say('How are you')
// 那么源代碼就要改了
var person = {
name: 'frank',
sayHi: function(){
console.log('Hi, I am' + this.name)
},
sayBye: function(){
console.log('Bye, I am' + this.name)
},
say: function(word){
console.log(word + ', I am' + this.name)
}
}
// 如果你不想吃語法糖
person.sayHi.call(person)
person.sayBye.call(person)
person.say.call(person, 'How are you')
// 還是回到那句話:this 是 call 的第一個(gè)參數(shù)
// this 是參數(shù),所以,只有在調(diào)用的時(shí)候才能確定
person.sayHi.call({name:'haha'}) // 這時(shí) sayHi 里面的 this 就不是 person 了
// this 真的很不靠譜
// 新手疑惑的兩種寫法
var fn = person.sayHi
person.sayHi() // this === person
fn() // this === window
sum(x,y){
return x+y
}
sum.call(undefined,1,2);//undefined必須寫
六、call / apply
n.call(asThis, p1,p2) 是函數(shù)的正常調(diào)用方式
當(dāng)你不確定參數(shù)的個(gè)數(shù)時(shí),就使用 apply
fn.apply(asThis, params)
function sum(){
var n = 0;
for(var i = 0;i<arguments.length;i++){
n+= arguments[i]
}
return n
}
sum(1,2,3,4,5,6);//21
//若調(diào)用為一個(gè)數(shù)組
var a = [1,2,3,4,5,6]
sum.call(undefined,a[0],a[1],a[2]...)
更新用apply:
sum.apply(undefined,a);//21
七、bind
call 和 apply 是直接調(diào)用函數(shù),而 bind 則是返回一個(gè)新函數(shù)(并沒有調(diào)用原來的函數(shù)),這個(gè)新函數(shù)會(huì) call 原來的函數(shù),call 的參數(shù)由你指定。
八、return
每個(gè)函數(shù)都有 return
如果你不寫 return,就相當(dāng)于寫了 return undefined
九、柯里化 / 高階函數(shù)
返回函數(shù)的函數(shù)
* 柯里化:將 f(x,y) 變成 f(x=1)(y) 或 f(y=1)x
//柯里化之前
function sum(x,y){
return x+y
}
//柯里化之后
function addOne(y){
return sum(1, y)
}
//柯里化之前
function Handlebar(template, data){
return template.replace('{{name}}', data.name)
}
//柯里化之后
function Handlebar(template){
return function(data){
return template.replace('{{name}}', data.name)
}
}
柯里化可以將真實(shí)計(jì)算拖延到最后再做
關(guān)于柯里化的高級(jí)文章:
* 高階函數(shù):
在數(shù)學(xué)和計(jì)算機(jī)科學(xué)中,高階函數(shù)是至少滿足下列一個(gè)條件的函數(shù):
a、 接受一個(gè)或多個(gè)函數(shù)作為輸入:forEach、sort、 map、 filter、reduce
b、輸出一個(gè)新函數(shù):bind、lodash.curry
fn.bind.call(fn,{},1,2,3)//
c、不過它也可以同時(shí)滿足兩個(gè)條件:Function.prototype.bind

[高階函數(shù)的應(yīng)用區(qū)別]https://blog.csdn.net/kingan123/article/details/79818566
偶數(shù)相加求和
array = [1,2,3,4,5,6,7,8]
var sum = 0;
for(var i= 0;i<array.length;i++){
if(array[i] % 2 ===0){
sum+=array[i]
}
}
console.log(sum)
法一:array.filter(function(n){n%2===0});//[2,4,6,8]
.reduce(function(prev,next){return prev+next})
法二:reduce(filter(array,function(n){n%2===0}),function(prev,next){return prev+next},0)
取奇數(shù)從小到大排序
array1 = [3,2,1,4,5,6,7,8]
sort(filter(array1,function(n){n%2===1}),function(a,b){return a-b})
接收一個(gè)函數(shù),叫高階函數(shù)
返回一個(gè)函數(shù),叫高階函數(shù)
函數(shù)被當(dāng)作參數(shù),叫回調(diào)
函數(shù)返回的函數(shù)參數(shù)比原函數(shù)少一個(gè)參數(shù)叫柯里化
十、回調(diào)
名詞形式:被當(dāng)做參數(shù)的函數(shù)就是回調(diào)
動(dòng)詞形式:調(diào)用這個(gè)回調(diào)
注意回調(diào)跟異步?jīng)]有任何關(guān)系
十一、構(gòu)造函數(shù)
返回對(duì)象的函數(shù)就是構(gòu)造函數(shù)
一般首字母大寫
new Number(1)//Number { 1 }
new String(1)
function Empty(){
this.name = '空'
return this //可不寫
}
var empty = new Empty;//寫new,自動(dòng)返回 Empty.call({})
十二、箭頭函數(shù)
箭頭函數(shù)沒有this,
setTimeout(function(){
console.log(this);//{name:'jack'}
}.bind({name:'jack'}),1000)
setTimeout(function(){
console.log(this);//window
},1000)
setTimeout(function(){
console.log(this);//{name:'jack'}
setTimeout(function(){
console.log(this);//window
},1000)
}.bind({name:'jack'}),1000)
setTimeout(function(){
console.log(this);//{name:'jack'}
setTimeout(function(){
console.log(this);//{name:'jack'}
}.bind(this),1000)
}.bind({name:'jack'}),1000)
//箭頭函數(shù)
setTimeout(function(){
console.log(this);//{name:'jack'}
setTimeout((a)=>console.log(this),1000);//{name:'jack'}
}.bind({name:'jack'}),1000)
十三、閉包
如果一個(gè)函數(shù),使用了他范圍外的值,那么(這個(gè)函數(shù)+這個(gè)變量)就叫做閉包
var a = 1;
function f(){
console.log(a)
}