1. 程式人生 > >【慕課網】JavaScript中函式和this

【慕課網】JavaScript中函式和this

1.函式概述

JS中函式比較特殊,函式也是物件中的一種。常叫做函式物件。

所以JS函式可以像其它物件那樣操作和傳遞,所以我們也常叫JS中的函式為函式物件。

函式的返回值依賴return,一般的函式呼叫:沒有return就會預設在所有程式碼執行完返回一個undefined;
如果作為構造器,外部使用new去呼叫,這樣沒有return語句或者return後邊是基本型別的話,那麼會將this作為返回;反之如果return了一個物件的話,這個物件作為new構造器的返回值。

函式4呼叫方式(不同調用方式this指向不同):

  • 直接呼叫 foo();
  • 物件方法 o.method();
  • 構造器 new Foo();
  • call/apply/bind func.call(o);

JavaScript核心:this,arguments,建構函式,作用域,不同調用方式,不同建立方法,閉包,原型鏈prototype,原型繼承_proto_

2.函式宣告與表示式

定義函式的三種形式:

1.函式宣告
function add(a,b){
	a = +a;
	b = +b;
	if(isNaN || isNaN(b)){
		return;
	}
	return a+b;
}

2.函式表示式
// function variable
var add = function(a,b){
	
};

//IEF(Immediately Executed Function) 立即執行函式表示式
(function(){

})();

//first-class function 作為返回值的函式表示式
return function(){

}

//NFE(Named Function Expression) 已命名的函式表示式
var add = function(a,b){

}

3.Function構造器
var func = new Function('a','b','console(a+b);')
func(1,2);   //3

var func = Function('a','b','console(a+b);')
func(1,2);   //3

在這裡插入圖片描述

附註:

一個完整語句,以function開頭,例如:

// 這是函式宣告
function foo() {

}

而用括號括起來的,或者前面有一元操作符的,都是函式表示式,例如:

// 函式表示式
(function foo() {

})

// 函式表示式
!function foo() {

}

// 函式表示式
+function foo() {

}

函式宣告和函式表示式的區別:函式宣告會被前置。

// 函式宣告(函式宣告被提前)
var num = add(1,2);
console.log(num);   // 3 

function add(a,b){
	return a+b;
}

// 函式表示式(變數add被提前,但是是undefined)
var num = add(1,2);
console.log(num);   //undefined is not a function

var add = function(a,b){
	return a+b;
}

在這裡插入圖片描述

Function構造器作用域和處理與一般函式有一定區別:

注:宣告未定義和未宣告的變數經過typeof都返回undefined

1.在函式構造器裡面定義的變數是區域性變數
Function('var localVal = "local";console.log(localVal);')();// 可以立即呼叫執行
console.log(typeof localVal);
// result:local,undefined (localVal仍為區域性變數)

2.不能訪問拿到外層函式的區域性變數localVal(可以拿到全域性物件,但是拿不到外層區域性變數)
var globalVal = 'global';
(function(){
    var localVal = 'local';
    Function('console.log(typeof localVal,typeof globalVal);')();
})();
// result: undefined,string (local不可訪問,全域性變數global可以訪問)

函式構造器(Constructor):

函式構造器就是可以用來構建生成新的函式或者物件的函式。

函式構造器也是一個普通函式,只不過在使用該函式構造生成新的函式或者物件的時候,該函式才會稱為函式構造器。

構建的過程簡單來說分兩步:

1、建立一個空物件,並執行類似建構函式bind該空物件的過程。也就是把建構函式中的this指向新的空物件然後執行一遍,這個過程可以叫初始化。

2、把空物件的原型指向建構函式的原型,也就是構建原型鏈繼承。

構建出的物件或者函式可以稱為建構函式的一個例項,因為在完成初始化的過程就設定新物件的許多私有屬性,而且該物件也繼承了構建函式的原型鏈,及上面的共有屬性。

3.this

this物件就是誰呼叫指向誰.

this在執行時才確定所指向的具體物件是誰。

1.當函式作為物件的方法呼叫時,this指向該物件。
2.當函式作為淡出函式呼叫時,this指向全域性物件(嚴格模式時,為undefined)
3.建構函式中的this指向新建立的物件
4.巢狀函式中的this不會繼承上層函式的this,如果需要,可以用一個變數儲存上層函式的this。
再總結的簡單點,如果在函式中使用了this,只有在該函式直接被某物件呼叫時,該this才指向該物件。

fn.call(o); //改變函式fn的作用域物件
fn.apply(o); //改變函式fn的作用域物件
fn.bind(o); //將函式fn的作用域繫結到物件o上

1.全域性作用域下this(瀏覽器):

console.log(this.document === document); //true
console.log(this === window); //true

this.a = 37;
console.log(window.a); //37

2.一般函式的this(瀏覽器):

全域性作用域下,this指向window,nodejs下是global;
嚴格模式下,this是undefined;

function f1(){
	return this;
}
f1() === window; //true,global object

function f2(){
	"use strict";
	return this;
}
f2() === undefined; //true

3.作為物件方法的函式this

this作為方法呼叫:作為物件方法呼叫 指向該物件

函式作為一個物件的屬性的值,也常常叫作一個物件的方法。

// 函式作為一個物件的屬性的值,也常常叫作一個物件的方法。
var o = {
	prop:37,
	f:function(){
		return this.prop;
	}
};
//呼叫物件的方法,該方法就是一個函式
console.log(o.f()); // 37 this指向物件o

var o = {prop:37};
function independent(){
	return this.prop;
}
// 函式賦給物件的屬性
o.f = independent;
console.log(o.f()); // 37

4.物件原型鏈上的this

物件原型鏈上的this,不管是原型鏈上的還是本身,都可以拿到.

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

get/set方法與this:指向get set方法所在的物件裡面

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.78 1.4142

5.構造器中的this

構造器中的this,若沒有return,this的指向會作為返回值.

函式的返回值依賴return,一般的函式呼叫:沒有return就會預設在所有程式碼執行完返回一個undefined;
如果作為構造器,外部使用new去呼叫,這樣沒有return語句或者return後邊是基本型別的話,那麼會將this作為返回;反之如果return了一個物件的話,這個物件作為new構造器的返回值。

function MyClass(){
	this.a =37;
}
var o = new MyClass();
console.log(o.a); //37

function C2(){
	this.a = 37;
	return {a:38};
}
o = new C2();
console.log(o.a); // 38

6.call/apply方法與this

call/apply方法與this的應用場景及用法,call/apply可以傳入一個物件作為函式作用域裡的this.

function add(c,d){
    return this.a + this.b + c + d;
}
var o = {a: 1,b: 3};
 
add.call(o, 5,7);//1+3+5+7 = 16
 
add.apply(o, [10,20]);// 1+3+10+20 = 34
 
function bar(){
    console.log(Object.prototype.toString.call(this));
}
bar.call(7);// "[object Number]"

7.bind方法與this

bind將一個物件繫結為某物件裡的(此處為函式)this後,被繫結的物件可以以特定的this,實現重複使用。

如這裡的g()函式

function f(){
    return this.a;
}
var g = f.bind({a:"test"});
console.log(g());//test
 
var o = {a:37,f:f,g:g};
console.log(o.f(),o.g());//37,test,使用bind繫結this

4.arguments

this始終指向的是呼叫者,當函式作為構造器時,this指向的新建立的空物件,然後就會執行構造器的所有程式碼,碰見this.XXX就是為新建物件新增屬性.如果構造器最後沒有顯示的使用return返回一個值,那麼就會返回this即新建物件.

函式屬性和arguments:

arguments是一個類陣列物件,原型不是Array.prototype,所以它沒有join等陣列物件才有的方法。

可以通過索引訪問arguments物件。

function foo(x,y,z){ //形參
    arguments.length;// 2 實參個數
    arguments[0];//1
    
    // 繫結關係
    arguments[0] = 10;
    x;//10  
    // 未傳引數,失去繫結關係
    arguments[2] = 100;
    z;//undefined  
    
    arguments.callee === foo;//true
}
 
foo(1,2)
foo.length;// 3 形參個數
foo.name;// "foo" 函式名
arguments.length 實參個數

在這裡插入圖片描述

apply/call方法(瀏覽器)

function foo(x,y){
    console.log(x,y,this);
}
 
foo.call(100,2,,3);// 2,3,Number(100)不是物件的會轉變成物件
foo.apply(true,[3,4]);// 3,4,Boolean
foo.apply(null);//undefined,undefined,window
foo.apply(undefined);//undefined,undefined,window

// 嚴格模式下
function foo(x,y){
   'use strict'; 
    console.log(x,y,this);
}
 
foo.apply(null);//undefined,undefined,null
foo.apply(undefined);//undefined,undefined,undefined

bind方法

this.x = 9;
var module = {
    x: 81,
    getX:function() {
        return this.x;
    }
}
 module.getX();//81物件.屬性
  
 var getX = module.getX;
 getX();// 9 直接賦值給變數呼叫
  
 var boundGetX = getX.bind(module);
 改變函式執行是的this,綁定了module
  
 bondGetX();// 81

bind與currying

function add(a,b,c){
	return a+b+c;
}

var func = add.bind(undefined,100);
func(1,2); // 103

// 100+200+10 =310
var func2 = func.bind(undefined,200);
func2(10); // 310

在這裡插入圖片描述

bind與new

function foo(){
    this.b = 100;
    return this.a;
}
 
var func = foo.bind({a: 1});
 
func();//1
new func();// {b:100}

bind方法模擬:

在這裡插入圖片描述