您好,登錄后才能下訂單哦!
轉(zhuǎn)載請注明出處
原文連接 http://blog.huanghanlian.com/article/5b698ef6b8ea642ea9213f4e
函數(shù)是一塊JavaScript代碼,被定義一次,但可執(zhí)行調(diào)用多次,js中的函數(shù)也是對象,所以js函數(shù)可以像其他對象那樣操作和傳遞所以我們也常叫js中的函數(shù)為函數(shù)對象。
函數(shù)的構(gòu)成主要有幾個(gè)部分函數(shù)名
,參數(shù)列表
,函數(shù)體
function foo(x, y) {
if (typeof x === 'number' && typeof y === 'number') {
return x + y;
} else {
return 0;
}
}
var cdr = foo(1,2);
console.log(cdr);//3
需要注意的是函數(shù)的返回值是依賴與return
語句的,如果沒有return
語句,默認(rèn)會(huì)在所有代碼執(zhí)行以后返回一個(gè)undefined
。那么這是一般的函數(shù)調(diào)用。
function foo(x, y) {
if (typeof x === 'number' && typeof y === 'number') {} else {
}
}
var cdr = foo(1, 2);
console.log(cdr);//undefined
如果是作為構(gòu)造器,外部使用new
去調(diào)用的話,如果沒有return
語句,或者return
是基本類型的話,那么會(huì)將this
作為返回
function foo(x, y) {
if (typeof x === 'number' && typeof y === 'number') {} else {
return x+y;
}
this.x=x+y;
}
var cdr = new foo(1, 2);
console.log(cdr.x);//3
重點(diǎn)
this
函數(shù)在不同的調(diào)用方式下他的this
指向是不一樣的,并且不同的調(diào)用方式下也會(huì)有一些細(xì)微的差別。
arguments
函數(shù)里面有一個(gè)特殊的對象,叫arguments
他和參數(shù)是有一定的連帶關(guān)系的。
function foo(a,b) {
return arguments;
}
var cdr=foo(1,2)
console.log(cdr);
不同的調(diào)用方式
直接調(diào)用 | 對象方法 | 構(gòu)造器 | call/apply/bind |
---|---|---|---|
foo() | o.method() | new Foo() | func.call(o); |
創(chuàng)建函數(shù)有不同的方式,常見的兩種就是函數(shù)聲明和函數(shù)表達(dá)式
函數(shù)聲明
function foo(a, b) {
a=+a;
b=+b;
if (isNaN(a)||isNaN(b)) { //isNaN() 函數(shù)用于檢查其參數(shù)是否是非數(shù)字值。是數(shù)字返回true,非數(shù)字返回false
return;
}
return a+b;
}
var cdr = foo(1, 2);
console.log(cdr);//3
函數(shù)表達(dá)式
把一個(gè)匿名函數(shù)賦值給一個(gè)變量,這種就是函數(shù)表達(dá)式
var add = function(a, b) {
//do sth
}
需要注意的是函數(shù)聲明的函數(shù)可以在函數(shù)前調(diào)用
add();
function add() {
console.log(1);//1
}
但是函數(shù)表達(dá)式會(huì)報(bào)錯(cuò)Uncaught TypeError: add is not a function
add();
var add = function() {
alert(1)
}
函數(shù)表達(dá)式-立即執(zhí)行函數(shù)表達(dá)式
把一個(gè)匿名函數(shù)用一個(gè)括號()括起來,然后再去直接調(diào)用,這種函數(shù)定義的方式呢,也叫作函數(shù)表達(dá)式,并且是立即執(zhí)行函數(shù)表達(dá)式。
(function() {
console.log(1); //1
})();
return 函數(shù)
我們也可以將函數(shù)對象,作為一個(gè)返回值,因?yàn)楹瘮?shù)也是對象。這種形式也是函數(shù)表達(dá)式。
return function(){
//do sth;
};
命名式函數(shù)表達(dá)式
這種并不常見,同樣賦值給給一個(gè)函數(shù),但是這個(gè)函數(shù)不是匿名函數(shù),而是有一個(gè)名字的函數(shù)。這就是命名式函數(shù)表達(dá)式
var add = function add() {
//do sth
};
除了函數(shù)聲明和函數(shù)表達(dá)式創(chuàng)建函數(shù)以外,還有一種不常見的一種創(chuàng)建函數(shù)對象的方式。就是函數(shù)構(gòu)造器Function構(gòu)造器
Function構(gòu)造器
函數(shù)構(gòu)造器就是大寫的Function
,
var add = new Function ('a','b','console.log(a+b);');
add(1,2);//3
var add = Function ('a','b','console.log(a+b);');
add(1,2);//3
可以通過new
或者直接調(diào)用的方式 運(yùn)行,他們倆基本沒什么區(qū)別,
前面兩個(gè)參數(shù)是函數(shù)對象里面的行參,最后一個(gè)參數(shù)表示函數(shù)體里面的代碼。
用參數(shù)是字符串會(huì)帶來安全上的一些隱患。
Function構(gòu)造器去創(chuàng)建的函數(shù)里面,創(chuàng)建的變量仍然是局部變量,也可以使用立即調(diào)用。
Function ('var lop="local";console.log(lop);')();//Function構(gòu)造器去創(chuàng)建的函數(shù)并且立即調(diào)用
console.log(lop);//lop是局部變量外部無法訪問會(huì)報(bào)錯(cuò)
在立即調(diào)用函數(shù)中有l(wèi)o變量,他的內(nèi)部有一個(gè)df函數(shù)聲明函數(shù)這個(gè)函數(shù)內(nèi)可以拿到lo變量
(function() {
var lo = "lo";
function df() {
console.log(lo)
}
df();
})();
//輸出lo
但是在Function構(gòu)造器中l(wèi)o卻訪問不到。
在立即調(diào)用函數(shù)里,lo不可訪問,全局變量l可以訪問。所以說Function構(gòu)造器很少使用
var l="l";
(function(){
var lo="lo";
Function ('console.log(l);console.log(lo);')();
})()
//l
//Uncaught ReferenceError: lo is not defined(…)
比一比三種創(chuàng)建函數(shù)的方式
函數(shù)聲明 | 函數(shù)表達(dá)式 | 函數(shù)構(gòu)造器 | |
---|---|---|---|
前置 | √ | ||
允許匿名 | √ | √ | |
立即調(diào)用 | √ | √ | |
在定義該函數(shù)的作用域通過函數(shù)名訪問 | √ | ||
沒有函數(shù)名 | √ |
前置:只有函數(shù)聲明會(huì)被前置,就好像被拉倒了最前面,說以說可以在函數(shù)聲明的位置之前,去調(diào)用這樣一個(gè)函數(shù)。
但是函數(shù)表達(dá)式和函數(shù)構(gòu)造器是代碼執(zhí)行階段,才會(huì)去創(chuàng)建對應(yīng)的函數(shù)對象,所以不會(huì)被前置。
允許匿名:函數(shù)表達(dá)式和函數(shù)構(gòu)造器都是允許匿名的,事實(shí)上函數(shù)構(gòu)造器只能是匿名的是不可以有名字的,但是函數(shù)聲明他的名字是不可以省略的,省略在比較新的瀏覽器會(huì)報(bào)錯(cuò)。
立即調(diào)用:函數(shù)聲明是不能被立即調(diào)用的,如果你嘗試把函數(shù)聲明后面寫一個(gè)括號,那么會(huì)報(bào)一個(gè)異常,因?yàn)楹瘮?shù)聲明被提前解析,并且前置放到最前了,所以會(huì)報(bào)錯(cuò)。函數(shù)表達(dá)式和函數(shù)構(gòu)造器因?yàn)椴粫?huì)前置,所以表達(dá)式計(jì)算結(jié)果是一個(gè)函數(shù)對象的時(shí)候我們可以加一對括號把他立即去調(diào)用。
在定義該函數(shù)的作用域通過函數(shù)名訪問:比如說fuction fd(){}
在function同級作用域下,可以通過fd()
去調(diào)用這個(gè)函數(shù),但是函數(shù)表達(dá)式和函數(shù)構(gòu)造器就不可以這樣。
在不同的環(huán)境下,同一個(gè)函數(shù)在不同的調(diào)用方式下,這個(gè) this
都有可能是不同的。
全局作用域下的this
全局作用域的 this
一般指向全局對象,那么在瀏覽器里面這個(gè)全局對象就是window
console.log(this.document === document); //true
console.log(this === window); //true
可以看到this.document
等價(jià)于window.document
this
等價(jià)于window
this.a = 37;
console.log(window.a); //37
給全局對象添加一個(gè)屬性a,這樣就相當(dāng)于創(chuàng)建了一個(gè)全局變量a,并且賦值為37。
一般函數(shù)的this(瀏覽器)
function f1() {
return this;
}
console.log(f1() === window); //true
var f1 = function() {
return this;
}
console.log(f1() === window); //true
用函數(shù)聲明或者函數(shù)表達(dá)式,返回this,這里的this指向全局對象。
var f2 = function() {
"use strict";
return this;
}
console.log(f2()); //undefined
需要注意的是再嚴(yán)格模式下那么默認(rèn)一般情況下this會(huì)指向
作為對象方法的函數(shù)的this
var o = {
prop: 37,
f: function() {
return this.prop;
}
};
console.log(o.f()); //37
創(chuàng)建了一個(gè)對象字面量o
,o
里面有一個(gè)屬性f
值呢是一個(gè)函數(shù)對象,對于一個(gè)把函數(shù)作為對象屬性值的方式,叫做對象的方法,作為對象的方法去調(diào)用o.f()
這種情況下this
會(huì)指向?qū)ο?code>o所以o.f()
被調(diào)用返回37。
var o = {
prop: 37
};
function indep() {
return this.prop;
}
o.f = indep;
console.log(o.f());//37
定義對象o
只有一個(gè)屬性prop
賦值為37
這里買你有一個(gè)獨(dú)立的indep
函數(shù),函數(shù)體return
一個(gè)this.prop;
如果直接去調(diào)用indep()
那么這個(gè)this會(huì)指向window
。
如果用賦值方式o.f
給o
對象創(chuàng)建屬性f
值為indep
函數(shù)對象,那么這樣去調(diào)用我們?nèi)稳荒軌蚰玫?7,console.log(o.f());//37
。
對象原型鏈上的this
Object.create({x:1});
是系統(tǒng)內(nèi)置的函數(shù),這個(gè)函數(shù)會(huì)接收一個(gè)參數(shù),一般是一個(gè)對象。他會(huì)返回一個(gè)新創(chuàng)建的對象,并且讓這個(gè)對象的原型指向參數(shù),參數(shù)一般是個(gè)對象。
var o = {
f: function() {
return this.a + this.b;
}
};
var p = Object.create(o);
p.a = 1;
p.b = 4;
console.log(p);
原型鏈上有f方法
var o = {
f: function() {
return this.a + this.b;
}
};
var p = Object.create(o);
p.a = 1;
p.b = 4;
console.log(p.f());//5
創(chuàng)建o對象屬性f值是一個(gè)函數(shù)對象,
通過var p = Object.create(o);
創(chuàng)建了一個(gè)對象p
,這個(gè)對象p
是一個(gè)空的對象,并且他的原型會(huì)指向o
,使用p.a
給對象p添加屬性值為1p.b
值為4,這樣就創(chuàng)建在對象上的屬性,那么我去調(diào)用p原型上的方法的時(shí)候,this.a + this.b;
任然能取到對象上的a和b。
get/set方法與this
function modulus() {
return Math.sqrt(this.re * this.re + this.im * this.im);
};
var o = {
re: 1,
im: -1,
get phase() {
return Math.atan2(this.im, this.re);
}
};
Object.defineProperty(o, 'modulus', {
get: modulus,
enumerable: true,
configurable: true
});
console.log(o.phase, o.modulus);//-0.7853981633974483 1.4142135623730951
構(gòu)造器中的this
function MyClass() {
this.a = 23;
};
var o = new MyClass();
console.log(o.a); //23
如果正常去調(diào)用一個(gè)函數(shù)的話MyClass()
那么這個(gè)函數(shù)的this
會(huì)指向window
但是如果我用new
實(shí)例化構(gòu)造函數(shù)去調(diào)用,那么這里面的this
會(huì)指向這樣一個(gè)空的對象,并且這個(gè)對象的原型會(huì)指向MyClass.prototype
最后這個(gè)this.a會(huì)作為返回值,因?yàn)檫@里沒有return
所以默認(rèn)this
會(huì)作為返回值,所以這個(gè)對象o就會(huì)就會(huì)有a屬性值23。
function c2() {
this.a = 23;
return {
a: 24
};
}
var f = new c2();
console.log(f.a); //24
c2函數(shù)this等于23,但是這一次return
返回了一個(gè)對象,那么這個(gè)a就不再是23了,而是返回的這個(gè)對象,o.a屬性就變成了24。
如果是作為構(gòu)造器,外部使用new去調(diào)用的話,如果沒有return語句,或者return是基本類型的話,那么會(huì)將this作為返回
call/apply方法與this
function add(c, d) {
return this.a + this.b + c + d;
};
var o = {
a: 1,
b: 3
};
var h=add.call(o, 5, 7);//1+3+5+7
console.log(h)
add.apply(o, [10, 20]);//1+3+10+20
function bar() {
console.log(Object.prototype.toString.call(this));
};
bar.call(7);//[object Number]
函數(shù)add,這里面返回this.a + this.b + c + d;
把這四個(gè)數(shù)相加起來,定義o對象有兩個(gè)屬性a和b,通過對象call
方法,.call(o, 5, 7)
第一個(gè)參數(shù)是你想作為this
的這樣一個(gè)對象,5和7是想要添加的參數(shù),5賦值給function add(c, d)
的c,7賦值給d;最終的結(jié)果就是1+3+5+7
,add.call(o, 5, 7)
和add.apply(o, [10, 20])
基本沒什么差別,只是apply
是數(shù)組作為傳參的。
bind方法與this
function f() {
return this.a;
};
var g = f.bind({
a: "test"
});
console.log(g()); //test
var o = {
a: 23,
f: f,
g: g
};
console.log(o.f(), o.g()); //23 "test"
在EcmaScript5中擴(kuò)展了叫bind的方法(IE6,7,8不支持)
這里有個(gè)函數(shù)對象f
,通過bind
方法,發(fā)現(xiàn)f.bind({ a: "test"});
有一個(gè)參數(shù),這個(gè)參數(shù)是一個(gè)對象,這個(gè)對象就是你想要將某一個(gè)對象作為this
的時(shí)候那就把這樣一個(gè)對象穿進(jìn)去,那么我們拿到一個(gè)新的g
對象,新的g
對象在調(diào)用的時(shí)候this
已經(jīng)指向了bind
的參數(shù),這對于我們需要去綁定一次重復(fù)調(diào)用仍然實(shí)現(xiàn)綁定,這樣會(huì)比call
,apply
會(huì)更加高效點(diǎn)。
還可以看到,這里有一個(gè)o
然后把a(bǔ)賦值為23,f屬性賦值為f函數(shù),g賦值綁定之后的方法,輸出f的時(shí)候能拿到23,一般的函數(shù)根據(jù)調(diào)用方式來判斷,他是通過對象屬性去調(diào)用的,那么這里買你的this就會(huì)指向o
那么就會(huì)拿到a:23。這里面比較特殊的我使用bind
的方法去綁定了之后,即使我們把 新綁定之后的方法作為對象的屬性去調(diào)用,仍然會(huì)按照之前的綁定去走。所以返回"test"
函數(shù)屬性 & arguments
接觸函數(shù)屬性和方法
function foo(a, b, d) {
console.log(arguments);
console.log(arguments.length); //2
console.log(arguments[0]); //1
arguments[1] = 10;
console.log(b); //change to 10;
arguments[2] = 100;
console.log(d); //即使手動(dòng)賦值任然是undefined. 沒有賦值arguments[2]=100;d等于undefined
console.log(arguments.callee === foo);
}
foo(1, 2);
console.log(foo.length);
console.log(foo.name);
console.dir(foo);
定義函數(shù)foo
通過foo.name
獲取函數(shù)名
通過foo.length
獲取函數(shù)行參個(gè)數(shù)
通過arguments.length
獲取函數(shù)實(shí)際傳參的個(gè)數(shù)
有一個(gè)問題。foo(1, 2);
傳參只有兩個(gè)參數(shù),實(shí)際上d
是沒有傳進(jìn)來的, 這種情況嘗試去arguments[2] = 100;
去賦值的時(shí)候?qū)?yīng)的d
仍然是undefined
嚴(yán)格模式下
function foo(a, b, d) {
'use strict'
console.log(arguments.length); //2
console.log(arguments[0]); //1
arguments[1] = 10;
console.log(b); //嚴(yán)格模式下仍然是1
arguments[2] = 100;
console.log(d); //即使手動(dòng)賦值任然是undefined. 沒有賦值arguments[2]=100;d等于undefined
console.log(arguments.callee === foo); //嚴(yán)格模式下arguments.callee是禁止使用
}
foo(1, 2);
console.log(foo.length);
console.log(foo.name);
apply/call方法
function foo(x, y) {
console.log(x, y, this);
};
foo.call(100, 1, 2); //1 2 Number {[[PrimitiveValue]]: 100}
foo.apply(true, [3, 4]); //3 4 Boolean {[[PrimitiveValue]]: true}
foo.apply(null); //undefined undefined Window {...}
foo.apply(undefined); //undefined undefined Window {...}
定義foo
函數(shù),有x,y兩個(gè)屬性,輸出參數(shù)和對應(yīng)的this
對于call()
來講第一個(gè)參數(shù)就是想作為this
的對象如果不是對象他會(huì)轉(zhuǎn)成對象,所以這里的foo.call(100, 1, 2);
100會(huì)轉(zhuǎn)成對應(yīng)的包裝類Number
值為100。類似的foo.apply(true, [3, 4]);
true會(huì)轉(zhuǎn)換成布爾值。
如果第一個(gè)參數(shù)是null
,undefined
的話,那么this
會(huì)指向全局對象,對于瀏覽器就是window對象
需要注意在嚴(yán)格模式下
function foo(x, y) {
'use strict';
console.log(x, y, this);
};
foo.call(100, 1, 2); //1 2 100
foo.apply(true, [3, 4]); //3 4 true
foo.apply(null); //undefined undefined null
foo.apply(undefined); //undefined undefined undefined
在嚴(yán)格模式下this
輸出直接量
bind方法
在EcmaScript5中擴(kuò)展了叫bind的方法(IE6,7,8不支持)
this.x = 9; //創(chuàng)建了一個(gè)全局變量x并賦值為9
var mod = {
x: 81,
getX: function() {
return this.x;
}
};
console.log(mod.getX()); //81
var gitX = mod.getX;
console.log(gitX());; //9
var bound = gitX.bind(mod);
console.log(bound());//81
創(chuàng)建了一個(gè)全局變量x
并賦值為9,然后有一個(gè)變量mod
賦值為一個(gè)對象字面量,里面有x屬性 值為81,有一個(gè)getX屬性,值是一個(gè)匿名函數(shù),他會(huì)返回this.x
。
如果對象(點(diǎn))屬性名 mod.getX()
這樣的調(diào)用方式。會(huì)返回這個(gè)對象為this
返回81。
var gitX = mod.getX;
如果定義一個(gè)全局變量gitX賦值為mod
對象的getX
屬性,這個(gè)時(shí)候this
就會(huì)指向全局對象,也就是this.x = 9;
所以返回9,console.log(gitX());; //9
通過bind
方法改變函數(shù)運(yùn)行時(shí)的this
指向,var bound = gitX.bind(mod);
也就是說gitX函數(shù)在運(yùn)行時(shí)this
為mod對象
。所以再次輸出81。
bind與currying 科里化功能
函數(shù)科里化就是把一個(gè)函數(shù)拆成多個(gè)單元。
function add(a,b,c){
return a+b+c;
};
var func=add.bind(undefined,100);
func(1,2);//103
var func2=func.bind(undefined,200);
func2(10);//310
這里有一個(gè)函數(shù)add
他的作用是把參數(shù)abc相加然后作為返回值,
可能有的時(shí)候不需要一次把函數(shù)都調(diào)用完,而是我調(diào)用一次把前面兩個(gè)參數(shù)傳完了后,得到這樣的函數(shù)我再去調(diào)用,傳入第三個(gè)值,比如通過add.bind()方法這次不需要改變this,那寫個(gè)undefined,提高額外參數(shù)100,這個(gè)我們拿到bind函數(shù)以后,相當(dāng)于這個(gè)100就會(huì)固定賦值給a第一個(gè)參數(shù),在調(diào)用func(1,2);
傳入1和2的時(shí)候,1就會(huì)給b,2就會(huì)給c,所以最后的答案就是103。
類似的再去func.bind()
也就是說綁定了兩個(gè)參數(shù)ab,b傳入200,再去調(diào)用一次func2(10)
傳入10,那么就是100+200+10,最終結(jié)果就是310。
實(shí)際例子
bind與new
function foo() {
this.b = 100;
return this.a;
};
var func = foo.bind({
a: 1
});
console.log(func()); //1
var func2 = new func();
console.log(func2); //{b: 100}
foo函數(shù)聲明,函數(shù)體this.b=100
和return this.a;
那如果我直接調(diào)用的話,this.b=100
中的this
是指向全局對象,所以說相當(dāng)于創(chuàng)建了一個(gè)全部變量b
并賦值為100,返回全局對象上的a
屬性。
使用bind()
方法,var func = foo.bind({ a: 1 });
來傳入一個(gè)參數(shù),一個(gè)字面量對象只有一個(gè)屬性a
,直接調(diào)用的話func()
,那么foo
函數(shù)中的this
就會(huì)指向bind({a:1})
的參數(shù)。所以調(diào)用func()
返回1。
如果使用new
的話就特殊些,使用new
除非func()
函數(shù)return 除非是對象
如果不是對象將會(huì)把this
作為返回值,并且this
會(huì)被初始化成一個(gè)默認(rèn)的空對象,這個(gè)空對象的原型是foo.prototype
,所以使用new
去調(diào)用的時(shí)候var func2 = new func();
,即使用了var func = foo.bind({ a: 1 });
bind方法但是這個(gè)this任然會(huì)指向沒有bind的時(shí)候的一樣,所以返回值會(huì)忽略return
,返回this
對象。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。