您的位置:首页 > 其它

函数调用模式4种方式详解

2017-03-15 22:17 183 查看

函数调用模式:

函数模式

特征:就是一个简单的函数调用,函数名前面没有任何的引导内容

function foo(){}
var func = function(){}

foo();
func();
(function(){})();


this
在函数模式中的含义:
this
在函数中表示全局对象,在浏览器中是window对象


方法模式

特征: 方法一定是依附于一个对象, 将函数赋值给对象的一个属性, 那么就成为了方法.

function f() {
this.method = function () {};
}

var o = {
method: function () {}
}


this
在方法模式调用中的含义:表示函数所依附的这个对象

构造器调用模式

由于构造函数只是给 this 添加成员. 没有做其他事情. 而方法也可以完成这个操作, 就 this 而言, 构造函数与方法没有本质区别.

特征:使用 new 关键字, 来引导构造函数.

function Person(){
this.name = "zhangsan";
this.age = 19;
this.sayHello = function(){
};
}
var p = new Person();
p.sayHello();


构造函数中发
this
与方法中一样, 表示对象, 但是构造函数中的对象是刚刚创建出来的对象

关于构造函数中
return
关键字的补充说明

构造函数中不需要
return
, 就会默认的
return this


如果手动的添加
return
, 就相当于 return this

如果手动的添加
return
基本类型; 无效, 还是保留原来 返回
this


如果手动添加
return null
; 或
return undefiend
, 无效

如果手动添加
return 对象类型
; 那么原来创建的
this
就会被丢掉, 返回的是
return
后面的对象

上下文调用模式

上下文(Context),就是函数调用所处的环境。

上下文调用,也就是自定义设置
this
的含义。

在其他三种调用模式中,函数/方法在调用的时候,this的值都是指定好了的,我们没办法自己进行设置,如果尝试去给
this
赋值,会报错。

上下文调用的语法–call、apply

//第一种, apply
函数名.apply(对象, [参数]);

//第二种, call
函数名.call(对象, 参数);

//上面两种方式的功能一模一样,只是在传递参数的时候有差异。


功能描述:

语法中的函数名表示的就是函数本身,使用函数调用模式的时候,
this
默认是全局对象

语法中的函数名也可以是方法(如:
obj.method
),在使用方法模式调用的时候,
this
默认是指当前对象

在使用
apply
call
的时候,默认的
this
都会失效,
this
的值由
apply
call
的第一个参数决定

补充说明

如果函数或方法中没有
this
的操作, 那么无论什么调用其实都一样.

如果是函数调用
foo()
, 那么有点像
foo.apply( window )
.

如果是方法调用
o.method()
, 那么有点像
o.method.apply( o )
.

参数问题

call
apply
在没有后面的参数的情况下(函数无参数, 方法无参数) 是完全一样的.

如下:

function foo() {
console.log( this );
}
foo.apply( obj );
foo.call( obj );


第一个参数的使用规则:

如果传入的是一个对象, 那么就相当于设置该函数中的 this 为参数

如果不传入参数, 或传入 null. undefiend 等, 那么相当于 this 默认为 window

foo();

foo.apply();

foo.apply( null );

foo.call( undefined );


如果传入的是基本类型, 那么 this 就是基本类型对应的包装类型的引用

number -> Number

boolean -> Boolean

string -> String

第二个参数的使用规则

在使用上下文调用的时候, 原函数(方法)可能会带有参数, 那么这个参数在上下文调用中使用第二个( 第 n 个 )参数来表示

function foo( num ) {
console.log( num );
}

foo.apply( null, [ 123 ] );

// 等价于
foo( 123 );


绑定函数bind

bind()最简单的用法是创建一个函数,使这个函数不论怎么调用都有同样的this值。常见的错误就像上面的例子一样,将方法从对象中拿出来,然后调用,并且希望this指向原来的对象。如果不做特殊处理,一般会丢失原来的对象。使用bind()方法能够很漂亮的解决这个问题:

//实现bind
Function.prototype._bind = function(thisObj) {
var fun = this;
var args = [].slice.call(arguments, 1); //bind函数的参数
return function() {
[].push.apply(args,arguments); //bind函数参数+调用时函数参数
return fun.apply(thisObj, args);
}
}
//应用案例
this.num = 9;
var module = {
num: 81,
getNum: function(a) { console.log(this.num,a,arguments); }
};

module.getNum(1); // 81

var getNum = module.getNum;
getNum(2); // 9, 因为在这个例子中,"this"指向全局对象

// 创建一个'this'绑定到module的函数
var boundGetNum = getNum._bind(module,4); //bind时给函数设置预定参数,下面调用函数时,可以再追加参数
boundGetNum(3); // 81,4,[4,3]


场景:setTimeout函数里面this指向全局,使用bind改变this指向

var name="12";
var obj = {
name: 'wei',
getName: function() {
setTimeout(function() {
console.log(this.name);
}, 1000);
},
bindGetname: function(){
setTimeout(function() {
console.log(this.name);
}.bind(this), 1000);
}
}
obj.getName();//12
obj.bindGetname(); //wei


上下文调用模式的应用

上下文调用只是能修改
this
, 但是使用的最多的地方上是函数借用.

1. 将伪数组转换为数组

伪数组转为数组:
/*方法一:var arr = [].slice.call(arguments);*/
/*方法二:var arr = [].concat.apply([],arguments);*/
/*方法三*/
var arr = [];
arr.push.apply(arr,arguments);
console.log(arr instanceof Array); //true
console.log(arr);


2. 求数组中的最大值

传统的做法

var max = arr[ 0 ];
for ( var i = 1; i < arr.length; i++ ) {
if ( arr[ i ] > max ) {
...
}
}


在 js 中的
Math
对象中提供了很多数学函数
Math.max( 1,2,3 )


还是利用 apply 可以展开数组的特性

var arr = [ 123456,12345,1234,345345,234,5 ];
Math.max.apply( null, arr );


3.借用构造函数继承

function Person ( name, age, gender ) {
this.name = name;
this.age = age;
this.gender = gender;
}

// 需要提供一个 Student 的构造函数创建学生对象
// 学生也应该有 name, age, gender, 同时还需要有 course 课程
function Student ( name, age, gender, course ) {
Person.call( this, name, age, gender );
this.course = course;
}


函数调用模式-知识点Test

函数调用模式有 函数调用、方法调用、构造函数调用,上下文调用

//1
var age = 38;
var obj = {
age: 18,
getAge: function() {
console.log(this.age);
}
};
var a = obj.getAge();
var getAge = obj.getAge;
getAge();

//18 38

//2
var age = 38;
var obj = {
age: 18,
getAge: function() {
console.log(this.age); //18,方法调用模式

function foo() {
console.log(this.age); //38,函数调用模式,this指向window
}
foo();
}
};
obj.getAge();

//3
var length = 10;
function fn(){
console.log(this.length);
}

var obj = {
length: 5,
method: function (fn) {
fn();   //  10 ,函数调用模式
fn.call(obj); //5, this指向obj
arguments[0](); //4 ,arguments[0]是fn,属于arguments对象,方法调用
}
};

obj.method(fn, 123, 456, 789);


function Foo() {
getName = function () { alert (1); };
return this;
}
Foo.getName = function () { alert (2);};
Foo.prototype.getName = function () { alert (3);};
var getName = function () { alert (4);};
function getName() { alert (5);}
Foo().getName();
new Foo().getName();


第一个是调用函数,修改了一开始定义的window.getName函数,Foo().getName()相当于window.getName,所以是1. new Foo()是实例对象,本身对象没有getName(),所以去调用原型的getName(),所以是3。

严格模式下的函数调用模式–this指向问题

'use strict'
var a=20;
function fn(){console.log(this.a)}
fn();

//非严格模式下,this指向window,结果为20
//严格模式下,this指向undefined,结果报错,Uncaught TypeError: Cannot read property 'a' of undefined

console.log("----------------");

var a=20;
function fn(){
function foo(){
console.log(this.a);
}
foo();
}
fn();

//非严格模式下,this指向window,结果为20
//严格模式下,this指向undefined,结果报错,Uncaught TypeError: Cannot read property 'a' of undefined


总结:在一个函数上下文中,this由调用者提供,由调用函数的方式来决定。如果调用者函数,被某一个对象所拥有,那么该函数在调用时,内部的this指向该对象(方法调用)。如果函数独立调用,那么该函数内部的this,则指向undefined(函数调用)。但是在非严格模式中,当this指向undefined时,它会被自动指向全局对象。

// 为了能够准确判断,我们在函数内部使用严格模式,因为非严格模式会自动指向全局
function fn() {
'use strict';
console.log(this);
}

fn();  // fn是调用者,独立调用(内部this指向undefined)
window.fn();  // fn是调用者,被window所拥有(内部this指向window)


但注意,

// demo03
'use strict'
var a = 20;
var obj = {
a: 10,
c: this.a + 20,
fn: function () {
return this.a;
}
}

console.log(obj.c); //40
console.log(obj.fn()); //10


对象obj中的c属性使用this.a + 20来计算,而他的调用者obj.c并非是一个函数。因此他不适用于上面的规则,我们要对这种方式单独下一个结论。

当obj在全局声明时,无论obj.c在什么地方调用,这里的this都指向全局对象,而当obj在函数环境中声明时,这个this指向undefined,在非严格模式下,会自动转向全局对象。可运行下面的例子查看区别。

'use strict';
var a = 20;
function foo () {
var a = 1;
var obj = {
a: 10,
c: this.a + 20,
fn: function () {
return this.a;
}
}
return obj.c;
}
console.log(foo());
// 运行会报错:Uncaught TypeError: Cannot read property 'a' of undefined


补充练习:

function foo() {
console.log(this.a)
}

function active(fn) {
fn(); // 真实调用者,为独立调用
}

var a = 20;
var obj = {
a: 10,
getA: foo
}

active(obj.getA);


非严格模式下,结果为: 20,

严格模式下:结果为:Uncaught TypeError: Cannot read property ‘a’ of undefined
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: