Javascript的函数调用
使用return语句时,函数会停止执行,并返回指定的值。
在 JavaScript 函数内部声明的变量(使用 var)是局部变量,所以只能在函数内部访问它。(该变量的作用域是局部的)。当函数被执行完之后就会被删除,自动被销毁。
您可以在不同的函数中使用名称相同的局部变量,因为只有声明过该变量的函数才能识别出该变量。
函数的参数也是局部变量。
只要函数运行完毕,本地变量就会被删除。
在函数外声明的变量是全局变量,全局变量会在页面关闭后被删除,被销毁。
向未声明的 JavaScript 变量分配值
如果您把值赋给尚未声明的变量,该变量将被自动作为全局变量声明。
这条语句:
carname="Volvo";
将声明一个全局变量 carname,即使它在函数内执行。
函数表达式
JavaScript 函数可以通过一个表达式定义,需要以分号结束。
函数表达式可以存储在变量中:
在函数表达式存储在变量后,变量也可作为一个函数使用:
var x = function (a, b) {return a * b};
var z = x(4, 3);
以上函数实际上是一个 匿名函数 (函数没有名称)。
函数存储在变量中,不需要函数名称,通常通过变量名来调用。
Function() 构造函数
在以上实例中,我们了解到函数通过关键字 function 定义。
函数同样可以通过内置的 JavaScript 函数构造器(Function())定义。
实例
var myFunction = new Function("a", "b", "return a * b");
var x = myFunction(4, 3);
实际上,你不必使用构造函数。上面实例可以写成:
实例
var myFunction = function (a, b) {return a * b}
var x = myFunction(4, 3);
在 JavaScript 中,很多时候,你需要避免使用 new 关键字。
而methods这一概念在JavaScript中的表现就是,一个对象的属性是一个function:同样的是函数,将其赋值给一个对象的成员以后,就不一样了。将函数赋值给对象的成员后,那么这个就不在称为函数,而应该叫做方法
函数提升?
1、var 变量预编译
JavaScript 的语法和 C 、Java、C# 类似,统称为 C 类语法。有过 C 或 Java 编程经验的同学应该对“先声明、后使用”的规则很熟悉,如果使用未经声明的变量或函数,在编译阶段就会报错。然而,JavaScript 却能够在变量和函数被声明之前使用它们。下面我们就深入了解一下其中的玄机。
先来看一段代码:
(function() {
console.log(noSuchVariable);//ReferenceError: noSuchVariable is not defined
})();
运行上面代码立马就报错,不过,这也正是我们期望的,因为 noSuchVariable 变量根本就没有定义过嘛!再来看看下面的代码:
(function() {
console.log(declaredLater); //undefined
var declaredLater = "Now it's defined!";
console.log(declaredLater);// "Now it's defined!"
})();
首先,上面这段代码是正确的,没有任何问题。但是,为什么不报错了?declaredLater 变量是在调用语句后面定义的啊?为什么居然输出的是 undefined?
这其实是 JavaScript 解析器搞的鬼,解析器将当前作用域内声明的所有变量和函数都会放到作用域的开始处,但是,只有变量的声明被提前到作用域的开始处了,而赋值操作被保留在原处。上述代码对于解析器来说其实是如下这个样子滴:
(function() {
var declaredLater; //声明被提前到作用域开始处了!
console.log(declaredLater); // undefined
declaredLater = "Now it's defined!"; //赋值操作还在原地!
console.log(declaredLater);//"Now it's defined!"
})();
这就是为什么上述代码不报异常的原因!变量和函数经过“被提前”之后,declaredLater 变量其实就被放在了调用函数的前面,根据 JavaScript 语法的定义,已声明而未被赋值的变量会被自动赋值为 undefined ,所以,第一次打印 declaredLater 变量的值就是 undefined,后面我们对 declaredLater 变量进行了赋值操作,所以,第二次再打印变量就会输出Now it's defined!。
再来看一个例子:
var name = "Baggins";
(function () {
console.log("Original name was " + name);// "Original name was undefined"
var name = "Underhill";
console.log("New name is " + name);// "New name is Underhill"
})();
上述代码中,我们先声明了一个变量 name ,我们的本意是希望在第一次打印 name 变量时能够输出全局范围内定义的 name 变量,然后再在函数中定义一个局部 name 变量覆盖全局变量,最后输出局部变量的值。可是第一次输出的结果和我们的预期完全不一致,原因就是我们定义的局部变量在其作用域内被“提前”了,也就是变成了如下形式:
var name = "Baggins";
(function () {
var name; //注意:name 变量被提前了!
console.log("Original name was " + name);// "Original name was undefined"
name = "Underhill";
console.log("New name is " + name);//"New name is Underhill"
})();
由于 JavaScript 具有这样的“怪癖”,所以建议大家将变量声明放在作用域的最上方,这样就能时刻提醒自己注意了。
2、函数声明“被提前”
前边说的是变量,接下来我们说说函数。
函数的“被提前”还要分两种情况,一种是函数声明,第二种是函数作为值赋值给变量,也即函数表达式。
先说第一种情况,上代码:
isItHoisted();//"Yes!"
function isItHoisted() {
console.log("Yes!");
}
如上所示,JavaScript 解释器允许你在函数声明之前使用,也就是说,函数声明并不仅仅是函数名“被提前”了,整个函数的定义也“被提前”了!所以上述代码能够正确执行。
再来看第二种情况:函数表达式形式。还是先上代码:
definitionHoisted();// "Definition hoisted!"
definitionNotHoisted();// TypeError: undefined is not a function
function definitionHoisted() {
console.log("Definition hoisted!");
}
var definitionNotHoisted = function () {
console.log("Definition not hoisted!");
};
我们做了一个对比,definitionHoisted 函数被妥妥的执行了,符合第一种类型;definitionNotHoisted 变量“被提前”了,但是他的赋值(也就是函数)并没有被提前,从这一点上来说,和前面我们所讲的变量“被提前”是完全一致的,并且,由于“被提前”的变量的默认值是 undefined ,所以报的错误属于“类型不匹配”,因为 undefined 不是函数,当然不能被调用。
总结
通过上面的讲解可以总结如下:
var的作用域是函数作用域由var定义的变量,它作用域在一个函数体内,而不是我们其他语言理解的大括号{ }内。
使用var声明的变量,其作用域为该语句所在的函数内,且存在变量提升现象;
变量的声明被提前到作用域顶部,赋值保留在原地
函数声明整个“被提前”
函数表达式时,只有变量“被提前”了,函数没有“被提前”
3、var的副作用
隐式全局变量和明确定义的全局变量间有些小的差异,就是通过delete操作符让变量未定义的能力。
通过var创建的全局变量(任何函数之外的程序中创建)是不能被删除的。
无var创建的隐式全局变量(无视是否在函数中创建)是能被删除的。
这表明,在技术上,隐式全局变量并不是真正的全局变量,但它们是全局对象的属性。属性是可以通过delete操作符删除的,而变量是不能的:
// 定义三个全局变量
var global_var = 1;
global_novar = 2; // 反面教材
(function () {
global_fromfunc = 3; // 反面教材
}());
// 试图删除
delete global_var; // false ,声明的变量不可以被删除
delete global_novar; // true,非严格模式下,未声明的变量可以被当做是属性可以删除
delete global_fromfunc; // true
// 测试该删除
typeof global_var; // "number"
typeof global_novar; // "undefined"
typeof global_fromfunc; // "undefined"
在ES5严格模式下,不管是已声明的全局变量,还是未声明的隐示全局变量,执行删除操作上会抛出一个错误。
使用 "use strict" 指令,开启严格模式。严格模式通过在脚本或函数的头部添加 "use strict"; 表达式来声明。在函数内部声明是局部作用域 (只在函数内使用严格模式)
严格模式下不能使用未声明的变量!
严格模式的限制有哪些??
4、单var形式声明变量
在函数顶部使用单var语句是比较有用的一种形式,其好处在于:
提供了一个单一的地方去寻找功能所需要的所有局部变量
防止变量在定义之前使用的逻辑错误
少代码(类型啊传值啊单线完成)
单var形式长得就像下面这个样子:
function func() {
var a = 1,
b = 2,
sum = a + b,
myobject = {},
i,
j;
// function body...
}
您可以使用一个var语句声明多个变量,并以逗号分隔。像这种初始化变量同时初始化值的做法是很好的。这样子可以防止逻辑错误(所有未初始化但声明的变量的初始值是undefined)和增加代码的可读性。在你看到代码后,你可以根据初始化的值知道这些变量大致的用途。
以上就是针对javascript的var预解析与函数声明提升的学习内容,希望对大家的学习有所帮助。
自调用函数?(4)
1 ( function(){alert(1);}() );
2 (function(){alert(1);}) ();
3 !function(){alert(1);}();
4 void function(){alert(2);}();
函数是对象
在 JavaScript 中使用 typeof 操作符判断函数类型将返回 "function" 。
但是JavaScript 函数描述为一个对象更加准确。
JavaScript 函数有 属性 和 方法。(arguments.length;??)
arguments.length 属性返回函数调用过程接收到的参数个数:
实例
function myFunction(a, b) {
return arguments.length;
}
toString() 方法将函数作为一个字符串返回:
实例
function myFunction(a, b) {
return a * b;
}
var txt = myFunction.toString();//function myFunction(a, b) { return a * b; }
函数参数?
js函数参数有显式参数(Parameters)即形参和隐式参数(Arguments)实参
1、显式参数(Parameters)即形参在函数定义时列出。
function functionName(parameter1, parameter2, parameter3){
//要执行的代码
}
2、隐式参数(Arguments)即实参在函数调用时传递给函数的真正的值
function add(){
console.log(arguments[0], arguments[1], arguments[2]);
}
add(1,2,3);//1 2 3
参数的个数
(1)当实参比比函数声明指定的形参个数少时,剩下的形参都将设置为undefined。
例:
function add(x, y){
console.log(x, y);
}
add(1);//1, undefined
(2)当实参比形参个数要多时,剩下的实参没办法直接获得,可以通过arguments对象来访问。在javascript中函数的参数在函数内部是用一个数组表示的。函数接收的始终都是这个数组,并不关心数组中包含哪些参数。而arguments是一个类数组对象,可以使用方括号语法来访问它的每一个元素。
例:
function add(x, y){
console.log(arguments[0], arguments[1], arguments[2]);
}
add(1, 2, 3);//1 2 3
arguments对象的length属性可以显示实参的个数,函数的length属性则显示形参的个数。
例:
function add(x, y){
console.log(arguments.length);//3}
add(1, 2, 3);
console.log(add.length);//2
形参可以提供便利,但不是必须的。
function add(){
console.log(arguments[0] + arguments[1]);
}
add(1, 2);//3
实参与形参同步
当形参与实参个数相同时,arguments对象的值和对应形参的值保持同步,但命名空间独立。
例:
function test(num1, num2){
console.log(num1, arguments[0]);//1 1
arguments[0] = 2;
console.log(num1, arguments[0]);//2 2
num1 = 10;
console.log(num1, arguments[0]);//10 10}
test(1);
在严格模式下,arguments对象的值和形参都是独立的。
例:
function test(num1, num2){
'use strict';
console.log(num1, arguments[0]);//1 1
argument[0] = 2;
comnsole.log(num1, arguments[0]);//1 2
num1 = 10;
console.log(num1, arguments[0]);//10 2}
test(1);
当形参并没有对应的实参时,arguments对象的值与形参的值并不对应。
例:
function test(num1, num2){
console.log(num1, arguments[0]);//undefined, undefined
num1 = 10;
arguments[0] = 5;
console.log(num1, arguments[0]);//10, 5}
test();
对象参数:当一个函数包含超过3个形参时,要记住调用函数中实参的正确顺序是一件很繁琐的事情,可以通过值对的形式来传入参数。
参数的传递
javascript中所有函数的参数都是按值传递的。也就是说,把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样,当传递的值是基本类型时,复制的是值本身。而参数为引用类型时,会把这个值在内存中的地址复制给一个局部变量。
(1)基本类型值的传递(传递的值是变量值的副本。)
在向参数传递基本类型的值时,被传递的值会被复制给一个局部变量(命名参数或arguments对象的一个元素)。
function add(num){
num += 10;
return num;
}var count = 20;
console.log(add(count));//30
console.log(count);//20
(2)引用类型值的传递(传递的值是一个指向对象的指针。)
在向参数传递引用类型的值时,会把存储在变量对象中的值复制一份放到为新变量分配的空间中,这个新变量的值是原对象的地址,也指向原对象。不同的是,这个值的副本实际上是一个指针,而这个指针指向存储在堆中的一个对象。复制操作结束后,两个变量实际上引用同一个对象,因此改变其中一个变量,就会影响另外一个变量。
var person = {
name:'Tom'
};function obj(peo){
peo.name = 'Jerry';
return peo;
}var result = obj(person);
console.log(result.name);//Jerry
console.log(person.name);//Jerry
在上面的例子中,把person(person是一个指向{name:‘Tom’}这个对象的指针)复制传入obj()中并赋值给了局部变量peo(按值传递),peo和person指向了同一个对象,而在obj()函数中peo指针通过引用修改了对象中的name属性,其实修改了person与peo共同指向的对象的name属性,相对应的外部person引用的name属性也就改变了,所以打印出来的为Jerry。
我们再看一个例子:
(3)共享类型值的传递
var person = {
name:'Tom'
};function obj(peo){
peo = {
name:'Jerry'
};
return peo;
}var result = obj(person);
console.log(result.name);//Jerry
console.log(person.name);//Tom
在上述例子中,与前一个并没有什么不同,我们只是在函数内对peo变量进行了重新赋值,将一个新对象的引用地址赋值给了peo,所以peo指向的是函数内部创建的新对象,和全部变量person指向老的对象是不一样的。
注:
(1)引用传递:指在调用函数时,即传递的是原对象引用的一个副本(原对象的地址),但是这个副本跟原对象的引用指向的都是内存中的对象!
(2)共享传递:在共享传递中对函数形参的直接赋值,也就是给形参直接赋值后,形参的值不再是原对象的地址了,指向了另外一个对象,因此不会影响实参的值,即原对象的值。
Arguments 对象?
JavaScript 函数有个内置的对象 arguments 对象。
javascript中的arguments对象
在js中一切都是对象,连函数也是对象,函数名其实是引用函数定义对象的变量。
1、什么是arguments?
这个函数体内的arguments非常特殊,实际上是所在函数的一个内置类数组对象,可以用数组的[i]和.length。arguments对象是比较特别的一个对象,实际上是当前函数的一个内置属性。arguments非常类似Array,但实际上又不是一个Array实例。
arguments.length是有实参决定,即函数调用时候里面的参数个数决定!
arguments对象的长度是由实参个数而不是形参个数决定的。形参是函数内部重新开辟内存空间存储的变量,但是其与arguments对象内存空间并不重叠。对于arguments和值都存在的情况下,两者值是同步的,但是针对其中一个无值的情况下,对于此无值的情形值不会得以同步。如下代码可以得以验证。
1 function f(a, b, c){
2 alert(arguments.length); // result: "2"
3 a = 100;
4 alert(arguments[0]); // result: "100"
5 arguments[0] = "qqyumidi";
6 alert(a); // result: "qqyumidi"
7 alert(c); // result: "undefined"
8 c = 2012;
9 alert(arguments[2]); // result: "undefined"
10 }
11
12 f(1, 2);
由JavaScript中函数的声明和调用特性,可以看出JavaScript中函数是不能重载的。
根据其他语言中重载的依据:"函数返回值不同或形参个数不同",我们可以得出上述结论:
第一:Javascript函数的声明是没有返回值类型这一说法的;
第二:JavaScript中形参的个数严格意义上来讲只是为了方便在函数中的变量操作,实际上实参已经存储在arguments对象中了。
另外,从JavaScript函数本身深入理解为什么JavaScript中函数是不能重载的:在JavaScript中,函数其实也是对象,函数名是关于函数的引用,或者说函数名本身就是变量。对于如下所示的函数声明与函数表达式,其实含义上是一样的(在不考虑函数声明与函数表达式区别的前提下),非常有利于我们理解JavaScript中函数是不能重载的这一特性。
function f(a){
return a + 10;
}
function f(a){
return a - 10;}
// 在不考虑函数声明与函数表达式区别的前提下,其等价于如下
var f = function(a){
eturn a + 10;}
var f = function(a){
return a - 10;}
2、有什么作用?
js语法不支持重载!但可用arguments对象模拟重载效果。
arguments对象:函数对象内,自动创建的专门接收所有参数值得类数组对象。
arguments[i]: 获得传入的下标为i的参数值
arguments.length: 获得传入的参数个数!
重载:
程序中可定义多个相同函数名,不同参数列表的函数,
调用者不必区分每个函数的参数,
执行时,程序根据传入的参数个数,自动判断选择哪个函数执行。
例子如下:
// 1、如果用户传入一个参数,求平方
function sum(a){
console.log(a*a);
}
//如果用户传入两个参数,就求和
function sum(a,b){
console.log(a+b);
}
sum(4); //?
sum(4,5); //?
上述例子中本意是想让同名函数sum()根据参数不同输出不同结果,但是sum是函数名字,本质也是个变量,
第二个会覆盖第一个,所以上述的正确输出答案是:NaN,9.所以这样显然不可以。
如果用arguments,就简单多了。
如下2个例子:
//2、
function calc(){
//如果用户传入一个参数,求平方
if(arguments.length==1){
console.log(arguments[0]*arguments[0]);
}else if(arguments.length==2){
//如果用户传入两个参数,就求和
console.log(arguments[0]+arguments[1]);
}
}
calc(4); //16
calc(4,5); //9
/*3、无论用户传入几个数字,都可以求和*/
function add(){
//arguments:[]
//遍历arguments中每个元素,并累加
for(var i=0,sum=0;i<arguments.length;sum+=arguments[i++]);
return sum;//返回和 }
console.log(add(1,2,3)); //6
console.log(add(1,2,3,4,5,6)); //21
这就是JS利用arguments重载的效果,简单理解就是一个函数重复利用.
arguments.length是有实参决定,即函数调用时候里面的参数个数决定!
4、arguments对象中有一个非常有用的属性:callee。arguments.callee返回此arguments对象所在的当前函数引用。在使用函数递归调用时推荐使用arguments.callee代替函数名本身。
如下:
function count(a){
if(a==1){
return 1;}
return a + arguments.callee(--a);
}
var mm = count(10);
alert(mm);
函数式编程的特性,??
函数的调用?
javascript的执行函数的四种方式
javascript的函数调用和构造函数调用
1 函数调用
Function绝对是JavaScript中的重中之重。在JavaScript中,Function承担了procedures, methods, constructors甚至是classes以及modules的功能。
在面向对象程序设计中,functions,methods以及class constructor往往是三件不同的事情,由不同的语法来实现。但是在JavaScript中,这三个概念都由function来实现,通过三种不同的模式。
最简单的使用模式就是function 调用:
function hello(username) {
return "hello, " + username;
}
hello("Keyser Söze"); // "hello, Keyser Söze"
/*函数调用模式*/
var add=function(a,b){
alert(this)//this被绑顶到window
return a+b;
}
var sum=add(3,4);
alert(sum)
2 方法调用
而methods这一概念在JavaScript中的表现就是,一个对象的属性是一个function,同样的是函数,将其赋值给一个对象的成员以后,就不一样了。将函数赋值给对象的成员后,那么这个就不在称为函数,而应该叫做方法。
var obj = {
hello: function() {
return "hello, " + this.username;
},
username: "Hans Gruber"
};
obj.hello(); // "hello, Hans Gruber"
/*方法调用模式*/
var myobject={
value:0,
inc:function(){
alert(this.value)
}
}
myobject.inc()
真正的行为是,调用本身才会决定this会绑定到哪个对象,即:
obj1.hello()会将this绑定到obj1,obj2.hello()则会将this绑定到obj2。记住一句话,谁是最后调用方法的,this就指向谁。
正因为this绑定的这种规则,在下面的用法也是可行的:
function hello() {
return "hello, " + this.username;
}
var obj1 = {
hello: hello,
username: "Gordon Gekko"
};
obj1.hello(); // "hello, Gordon Gekko"
var obj2 = {
hello: hello,
username: "Biff Tannen"
};_
obj2.hello(); // "hello, Biff Tannen"
但是,在一个普通的函数中,如上面的hello函数,使用this关键字是不太好的方式,当它被直接调用的时候,this的指向就成了问题。在这种情况下,this往往被指向全局对象(GlobalObject),在浏览器上一般就是window对象。
而这种行为是不确定和没有意义的。
所以在ES5标准中,如果使用了strict mode,那么this会被设置为undefined:
function hello() {
"use strict";
return "hello, " + this.username;
}
hello(); // error: cannot read property "username" of undefined
以上这种做法是为了让潜在的错误更快的暴露出来,避免了误操作和难以找到的bug。
区别普通函数调用和方法调用,直接看这个例子就明确了。
var func = function() {
alert(this);
};
var o = {};
o.fn = func;//”=”号给o对象自身添加一个属性,并把一个函数赋值给该属性,也即是方法。
// 比较
alert(o.fn === func);//true
// 调用
func();//[object Window]
o.fn();//[object Object]
这里的运行结果是,两个函数是相同的,因此打印结果是 true。但是由于两个函数的调用是不一样的,func 的调用,打印的是 [object Window],而o.fn 的打印结果是 [object Object]。
这里便是函数调用与方法调用的区别,函数调用中,this 专指全局对象 window,而在方法中 this 专指当前对象,即 o.fn 中的 this 指的就是对象o。
3 构造函数
function的第三种使用模式就是讲它作为constructor:
构造器中的this
我们需要分析创建对象的过程,方能知道this的意义. 如下面代码:
/*构造器调用模式 摒弃*/
var quo=function(string){
this.status=string;
}
quo.prototype.get_status=function(){
return this.status;
}
var qq=new quo("aaa");
alert(qq.get_status());
var Person = function() {
this.name = "小平果";
};
var p = new Person();
这里首先定义了函数Person,下面分析一下整个执行:
程序在执行到第一句的时候,不会执行函数体,因此 JavaScript的解释器并不知道这个函数的内容.
接下来执行new关键字,创建对象,解释器开辟内存,得到对象的引用,将新对象的引用交给函数.
紧接着执行函数,将传过来的对象引用交给this. 也就是说,在构造方法中,this就是刚刚被new创建出来的对象.
然后为this 添加成员,也就是为对象添加成员.
最后函数结束,返回this,将this交给左边的变量.
分析过构造函数的执行以后,可以得到,构造函数中的this就是当前对象.
构造器中的return
在构造函数中return的意义发生了变化,首先如果在构造函数中,如果返回的是一个对象,那么就保留原意. 如果返回的是非对象,比如数字、布尔和字符串,那么就返回this,如果没有return语句,那么也返回this. 看下面代码:
// 返回一个对象的 return
var ctr = function() {
this.name = "赵晓虎";
return {
name:"牛亮亮"
};
};
// 创建对象
var p = new ctr();
// 访问name属性
alert(p.name);
//执行代码,这里打印的结果是”牛亮亮”. 因为构造方法中返回的是一个对象,那么保留return的意义,返回内容为return后面的对象. 再看下面代码:
// 定义返回非对象数据的构造器
var ctr = function() {
this.name = "赵晓虎";
return "牛亮亮";
};
// 创建对象
var p = new ctr();
// 使用
alert(p);
alert(p.name);
代码运行结果是,先弹窗打印[object Object],然后打印”赵晓虎”. 因为这里 return 的是一个字符串,属于基本类型,那么这里的return语句无效,返回的是this对象. 因此第一个打印的是[object Object]而第二个不会打印undefined.
function User(name, passwordHash) {
this.name = name;
this.passwordHash = passwordHash;
}
var u = new User("sfalken",
"0ef33ae791068ec64b502d6cb0191387");
u.name; // "sfalken"
使用new关键将function作为constructor进行调用。和function以及method调用不一样的是,constructor会传入一个新的对象并将它绑定到this,然后返回该对象作为constructor的返回值。而constructor function本身的作用就是为了初始化该对象。
构造函数调用常犯的一个错误
兴致勃勃地定义了下面这么个构造函数:
var Coder = function( nick ){
this.nick = nick;
};
定义构造函数结束后呢?没错,赶紧实例化:
var coder = Coder( 'casper' );
- 1
这个coder兄弟叫什么名字?赶紧打印下:
console.log( coder.nick ); //undefined
竟然是undefined!!再回过头看看实例化的那个语句,不难发现问题出在哪里:少了个new
var coder = Coder( 'casper' ); //当作普通的函数来调用,故内部的this指针其实指向window对象
console.log( window.nick); //输出:casper
var coder = new Coder( 'casper' ); //加上new,一切皆不同,this正确地指向了当前创建的实例
console.log( coder.nick ); //输出:casper
这样的错误貌似挺低级的,但出现的概率挺高的,肿么去避免或减少这种情况的发生呢?
可以在内部实现里面动下手脚:
var Coder = function( nick ){
if( !(this instanceof Coder) ){
return new Coder( nick );
}
this.nick = nick;
};
其实很简单,实例化的时候,内部判断下,当前this指向的对象的类型即可,如果非当前构造函数的类型,强制重新调用一遍构造函数。
突然觉得Coder这名字不够洋气?想用Hacker,好吧,我改。。。数了下,一共有三处要改,这不科学,有没有办法只把构造函数的名字改了就行?
当然有:
var Coder = function( nick ){
if( !(this instanceof arguments.callee) ){
return new arguments.callee( nick );
}
this.nick = nick;
};
4 间接调用
在 JavaScript 中, 函数是对象。JavaScript 函数有它的属性和方法。
call() 和 apply() 是预定义的函数方法。 两个方法可用于调用函数,两个方法的第一个参数必须是对象本身。
区分apply,call就一句话,
foo.call(this, arg1,arg2,arg3) == foo.apply(this, arguments)==this.foo(arg1, arg2, arg3)
1
例如:
A, B类都有一个message属性(面向对象中所说的成员),
A有获取消息的getMessage方法,
B有设置消息的setMessage方法,
var b = new B();
//给对象a动态指派b的setMessage方法,注意,a本身是没有这方法的!
b.setMessage.call(a, "a的消息");
//下面将显示"a的消息"
alert(a.getMessage());
//给对象b动态指派a的getMessage方法,注意,b本身也是没有这方法的!
alert(b.setMessage());
这就是动态语言 JavaScript call的威力所在!
简直是”无中生有”,对象的方法可以任意指派,而对象本身一直都是没有这方法的,注意是指派,通俗点就是,方法是借给另一个对象的调用去完成任务,原理上是方法执行时上下文对象改变了.
所以 b.setMessage.call(a, “a的消息”); 就等于用a作执行时上下文对象调用b对象的setMessage方法,而这过程中与b一点关系都没有, 作用等效于a.setMessage( “a的消息”);
call, apply作用就是借用别人的方法来调用,就像调用自己的一样.
好,理解了call, apply相同处—–作用后,再来看看它们的区别,看过上面例子,相信您大概知道了.
从 b.setMessage.call(a, “a的消息”) 等效于 a.setMessage( “a的消息”) 可以看出, “a的消息”在call中作为一个参数传递,
call, apply方法区别是,从第二个参数起, call方法参数将依次传递给借用的方法作参数, 而apply直接将这些参数放到一个数组中再传递, 最后借用方法的参数列表是一样的.当参数明确时可用call, 当参数不明确时可用apply给合arguments