Js基础知识5-函数返回值、函数参数、函数属性、函数⽅法
函数返回值
所有函数都有返回值,没有return语句时,默认返回内容为undefined,和其他⾯向对象的编程语⾔⼀样,return语句不会阻⽌finally⼦句的执⾏。
function testFinnally(){
try{
return 2;
}catch(error){
return 1;
}finally{
return 0;
}
}
testFinnally();//0
如果函数调⽤时在前⾯加上了new前缀,且返回值不是⼀个对象,则返回this(该新对象)。
function fn(){
this.a = 2;
return 1;
}
var test = new fn();
console.log(test);//{a:2}
console.structor);//fn(){this.a = 2;return 1;}
如果返回值是⼀个对象,则返回该对象。
function fn(){
this.a = 2;
return {a:1};
}
var test = new fn();
console.log(test);//{a:1}
console.structor);//Object() { [native code] }
函数参数
arguments
javascript中的函数定义并未指定函数形参的类型,函数调⽤也未对传⼊的实参值做任何类型检查。实际上,javascript函数调⽤甚⾄不检查传⼊形参的个数。
function add(x){
return x+1;
}
console.log(add(1));//2
console.log(add('1'));//'11'
console.log(add());//NaN
console.log(add(1,2));//2
同名形参
在⾮严格模式下,函数中可以出现同名形参,且只能访问最后出现的该名称的形参。
function add(x,x,x){
return x;
}
console.log(add(1,2,3));//3
⽽在严格模式下,出现同名形参会抛出语法错误
function add(x,x,x){
'use strict';
return x;
}
console.log(add(1,2,3));//SyntaxError: Duplicate parameter name not allowed in this context
参数个数
当实参⽐函数声明指定的形参个数要少,剩下的形参都将设置为undefined值
function add(x,y){
console.log(x,y);//1 undefined
}
add(1);
常常使⽤逻辑或运算符给省略的参数设置⼀个合理的默认值
function add(x,y){
y = y || 2;
console.log(x,y);//1 2
}
add(1);
[注意]实际上,使⽤y || 2是不严谨的,显式地设置假值(undefined、null、false、0、-0、”、NaN)也会得到相同的结果。所以应该根据实际场景进⾏合理设置
当实参⽐形参个数要多时,剩下的实参没有办法直接获得,需要使⽤即将提到的arguments对象
javascript中的参数在内部⽤⼀个数组表⽰。函数接收到的始终都是这个数组,⽽不关⼼数组中包含哪些参数。在函数体内可以通过arguments对象来访问这个参数数组,从⽽获取传递给函数的每⼀个参数。arguments对象并不是Array的实例,它是⼀个类数组对象,可以使⽤⽅括号语法访问它的每⼀个元素
function add(x){
console.log(arguments[0],arguments[1],arguments[2])//1 2 3
return x+1;
}
add(1,2,3);
arguments对象的length属性显⽰实参的个数,函数的length属性显⽰形参的个数
function add(x,y){
console.log(arguments.length)//3
return x+1;
}
add(1,2,3);
console.log(add.length);//2
形参只是提供便利,但不是必需的
function add(){
return arguments[0] + arguments[1];
}
console.log(add(1,2));//3
对象参数
当⼀个函数包含超过3个形参时,要记住调⽤函数中实参的正确顺序实在让⼈头疼
function arraycopy(/*array*/from,/*index*/form_start,/*array*/to,/*index*/to_start,/*integer*/length){
//todo
}
通过名/值对的形式来传⼊参数,这样参数的顺序就⽆关紧要了。定义函数的时候,传⼊的实参都写⼊⼀个单独的对象之中,在调⽤的时候传⼊⼀个对象,对象中的名/值对是真正需要的实参数据
function easycopy(args){
arraycopy(args.from,args.from_start || ,_start || 0, args.length);
}
var a = [1,2,3,4],b =[];
easycopy({from:a,to:b,length:4});
以函数为参数
函数本⾝是⼀个对象,因此可以将函数作为另⼀个函数的参数,进⽽实现函数回调,功能等同于c++中的函数指针
function printf(str){
dom1.innerText += String()+"\n"; //设置dom1显⽰的⽂字。变量也可以⾃动调⽤其他js⽂件中的dom1变量。dom1会先在当前⽂件中查询,然后向之前引⽤的js⽂件查询,再向之后引⽤的js⽂件查询}
function callfunction(myfunction,myargument){ //函数作为其他函数的参数
return myfunction(myargument); //调⽤回调函数
}
callfunction(printf,"hello world");
同步
当形参与实参的个数相同时,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对象的值相同,但并不是相同的命名空间。它们的命名空间是独⽴的,但值是同步的
但在严格模式下,arguments对象的值和形参的值是独⽴的
function test(num1,num2){
'use strict';
console.log(num1,arguments[0]);//1 1
arguments[0] = 2;
console.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();
内部属性【callee】
arguments对象有⼀个名为callee的属性,该属性是⼀个指针,指向拥有这个arguments对象的函数
下⾯是经典的阶乘函数
function factorial(num){
if(num <=1){
return 1;
}else{
return num* factorial(num-1);
}
}
console.log(factorial(5));//120
但在严格模式下,访问这个属性会抛出TypeError错误
function factorial(num){
'use strict';
if(num <=1){
return 1;
}else{
return num* arguments.callee(num-1);
}
}
//TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
console.log(factorial(5));
这时,可以使⽤具名的函数表达式
var factorial = function fn(num){
if(num <=1){
return 1;
}else{
return num*fn(num-1);
}
};
console.log(factorial(5));//120
【caller】
实际上有两个caller属性
【1】函数的caller
函数的caller属性保存着调⽤当前函数的函数的引⽤,如果是在全局作⽤域中调⽤当前函数,它的值是null
function outer(){
inner();
}
function inner(){
console.log(inner.caller);//outer(){inner();}
}
outer();
function inner(){
console.log(inner.caller);//null
}
inner();
在严格模式下,访问这个属性会抛出TypeError错误
function inner(){
'use strict';
//TypeError: 'caller' and 'arguments' are restricted function properties and cannot be accessed in this context
console.log(inner.caller);
}
inner();
【2】arguments对象的caller
该属性始终是undefined,定义这个属性是为了分清arguments.caller和函数的caller属性
function inner(x){
console.log(arguments.caller);//undefined
}
inner(1);
同样地,在严格模式下,访问这个属性会抛出TypeError错误
function inner(x){
'use strict';
//TypeError: 'caller' and 'arguments' are restricted function properties and cannot be accessed in this context
console.log(arguments.caller);
}
inner(1);
函数重载
javascript函数不能像传统意义上那样实现重载。⽽在其他语⾔中,可以为⼀个函数编写两个定义,只要这两个定义的签名(接受的参数的类型和数量)不同即可 javascript函数没有签名,因为其参数是由包含0或多个值的数组来表⽰的。⽽没有函数签名,真正的重载是不可能做到的
//后⾯的声明覆盖了前⾯的声明
function addSomeNumber(num){
return num + 100;
}
function addSomeNumber(num){
return num + 200;
}
var result = addSomeNumber(100);//300
只能通过检查传⼊函数中参数的类型和数量并作出不同的反应,来模仿⽅法的重载
function doAdd(){
if(arguments.length == 1){
alert(arguments[0] + 10);
}else if(arguments.length == 2){
alert(arguments[0] + arguments[1]);
}
}
doAdd(10);//20
doAdd(30,20);//50
参数传递
javascript中所有函数的参数都是按值传递的。也就是说,把函数外部的值复制到函数内部的参数,就和把值从⼀个变量复制到另⼀个变量⼀样
【1】基本类型值
在向参数传递基本类型的值时,被传递的值会被复制给⼀个局部变量(命名参数或arguments对象的⼀个元素)
function addTen(num){
num += 10;
return num;
}
var count = 20;
var result = addTen(count);
console.log(count);//20,没有变化
console.log(result);//30
【2】引⽤类型值
在向参数传递引⽤类型的值时,会把这个值在内存中的地址复制给⼀个局部变量,因此这个局部变量的变化会反映在函数的外部
function setName(obj){
obj.name = 'test';
}
var person = new Object();
setName(person);
console.log(person.name);//'test'
当在函数内部重写引⽤类型的形参时,这个变量引⽤的就是⼀个局部对象了。⽽这个局部对象会在函数执⾏完毕后⽴即被销毁
function setName(obj){
obj.name = 'test';
console.log(person.name);//'test'
obj = new Object();
obj.name = 'white';
js argumentsconsole.log(person.name);//'test'
}
var person = new Object();
setName(person);
函数属性
【length属性】
arguments对象的length属性表⽰实参个数,⽽函数的length属性则表⽰形参个数
function add(x,y){
console.log(arguments.length)//3
console.log(add.length);//2
}
add(1,2,3);
【name属性】
函数定义了⼀个⾮标准的name属性,通过这个属性可以访问到给定函数指定的名字,这个属性的值永远等于跟在function关键字后⾯的标识符,匿名函数的name属性为空
//IE11-浏览器⽆效,均输出undefined
//chrome在处理匿名函数的name属性时有问题,会显⽰函数表达式的名字
function fn(){};
console.log(fn.name);//'fn'
var fn = function(){};
console.log(fn.name);//'',在chrome浏览器中会显⽰'fn'
var fn = function abc(){};
console.log(fn.name);//'abc'
[注意]name属性早就被浏览器⼴泛⽀持,但是直到ES6才将其写⼊了标准
ES6对这个属性的⾏为做出了⼀些修改。如果将⼀个匿名函数赋值给⼀个变量,ES5的name属性,会返回空字符串,⽽ES6的name属性会返回实际的函数名
var func1 = function () {};
func1.name //ES5: ""
func1.name //ES6: "func1"
如果将⼀个具名函数赋值给⼀个变量,则ES5和ES6的name属性都返回这个具名函数原本的名字
var bar = function baz() {};
bar.name //ES5: "baz"
bar.name //ES6: "baz"
Function构造函数返回的函数实例,name属性的值为“anonymous”
(new Function).name // "anonymous"
bind返回的函数,name属性值会加上“bound ”前缀
function foo() {};
foo.bind({}).name // "bound foo"
(function(){}).bind({}).name // "bound "
【prototype属性】
每⼀个函数都有⼀个prototype属性,这个属性指向⼀个对象的引⽤,这个对象称做原型对象(prototype object)。每⼀个函数都包含不同的原型对象。将函数⽤做构造函数时,新创建的对象会从原型对象上继承属性
function fn(){};
var obj = new fn;
fn.prototype.a = 1;
console.log(obj.a);//1
函数⽅法
【apply()和call()】
每个函数都包含两个⾮继承⽽来的⽅法:apply()和call()。这两个⽅法的⽤途都是在特定的作⽤域中调⽤函数,实际上等于函数体内this对象的值
要想以对象o的⽅法来调⽤函数f(),可以这样使⽤call()和apply()
f.call(o);
f.apply(o);
假设o中不存在m⽅法,则等价于:
o.m = f; //将f存储为o的临时⽅法
o.m(); //调⽤它,不传⼊参数
delete o.m; //将临时⽅法删除
下⾯是⼀个实际的例⼦
var o = {color: "blue"};
function sayColor(){
console.lor);
}
sayColor(); //red
sayColor.call(this); //red
sayColor.call(window); //red
sayColor.call(o); //blue
//sayColor.call(o)等价于:
o.sayColor = sayColor;
o.sayColor(); //blue
delete o.sayColor;
apply()⽅法接收两个参数:⼀个是在其中运⾏函数的作⽤域(或者可以说成是要调⽤函数的母对象,它是调⽤上下⽂,在函数体内通过this来获得对它的引⽤),另⼀个是参数数组。其中,第⼆个参数可以是Array的实例,也可以是arguments对象
function sum(num1, num2){
return num1 + num2;
}
//因为运⾏函数的作⽤域是全局作⽤域,所以this代表的是window对象
function callSum1(num1, num2){
return sum.apply(this, arguments);
}
function callSum2(num1, num2){
return sum.apply(this, [num1, num2]);
}
console.log(callSum1(10,10));//20
console.log(callSum2(10,10));//20
call()⽅法与apply()⽅法的作⽤相同,它们的区别仅仅在于接收参数的⽅式不同。对于call()⽅法⽽⾔,第⼀个参数是this值没有变化,变化的是其余参数都直接传递给函数。换句话说,在使⽤call()⽅法时,传递给函数的参数必须逐个列举出来
function sum(num1, num2){
return num1 + num2;
}
function callSum(num1, num2){
return sum.call(this, num1, num2);
}
console.log(callSum(10,10)); //20
⾄于是使⽤apply()还是call(),完全取决于采取哪种函数传递参数的⽅式最⽅便。如果打算直接传⼊arguments对象,或者包含函数中先接收到的也是⼀个数组,那么使⽤apply()肯定更⽅便;否则,选择call()可能更合适
在⾮严格模式下,使⽤函数的call()或apply()⽅法时,null或undefined值会被转换为全局对象。⽽在严格模式下,函数的this值始终是指定的值
var color = 'red';
function displayColor(){
console.lor);
}
displayColor.call(null);//red
var color = 'red';
function displayColor(){
'use strict';
console.lor);
}
displayColor.call(null);//TypeError: Cannot read property 'color' of null
应⽤
【1】调⽤对象的原⽣⽅法
var obj = {};
obj.hasOwnProperty('toString');// false
obj.hasOwnProperty = function (){
return true;
};
obj.hasOwnProperty('toString');// true
Object.prototype.hasOwnProperty.call(obj, 'toString');// false
【2】出数组最⼤元素
javascript不提供出数组最⼤元素的函数。结合使⽤apply⽅法和Math.max⽅法,就可以返回数组的最⼤元素
var a = [10, 2, 4, 15, 9];
Math.max.apply(null, a);//15
【3】将类数组对象转换成真正的数组
Array.prototype.slice.apply({0:1,length:1});//[1]
或者
[].prototype.slice.apply({0:1,length:1});//[1]
【4】将⼀个数组的值push到另⼀个数组中
var a = [];
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论