函数
JS对象与函数关系更近,与Java类与方法分明有所不同,JS中的函数也就是对象。
函数也可以被定义在其他函数中,一个内部函数除了可以访问自己的参数和变量,也可以访问把它嵌套在其中的父函数的参数和变量。通过函数字面量创建的函数对象包含一个连接到外部上下文的连接。这被称作闭包。
调用
调用一个函数会暂停当前函数的执行,传递控制权和参数给新函数。除了声明时定义的形式参数,每个函数还接受两个附加参数:this
和arguments
。
参数this在面向对象编程中非常重要,他的只取决于调用的模式。在JS中一共有4种调用模式:方法调用模式、函数调用模式、构造器调用模式、apply调用模式。这些模式在如何初始化关键参数this上存在差异。
方法调用模式
当一个函数被保存为一个对象的属性时,我们称它为一个方法。当一个方法被调用时,this被绑定到该对象。
var myObject = {
value: 0,
increnment: function(){
this.value += 1;
}
}
myObject.increnment();
console.log(myObject.value); //=>1
上述例子中this.value就相当于myObject.value。
函数调用模式
当一个函数直接被调用时就是函数调用模式,this指向全局对象window。
var value = 1;
function increnment(){
this.value += 1;
}
var myObject = {
value: 0,
callIncrement: function(){
increnment();
}
}
myObject.callIncrement();
console.log(value); //=>2
console.log(myObject.value); //=>0
当执行到increment方法时,因为this指向的是全局value,不是myObject.value,所以结果是window.value值发生了改变,而myObject.value不变。
使用that来解决这个问题:
var value = 1;
function increnment(obj){
obj.value += 1;
}
var myObject = {
value: 0,
callIncrement: function(){
var that = this;
increnment(that);
}
}
myObject.callIncrement();
console.log(value); //=>1
console.log(myObject.value); //=>1
构造器调用模式
通过new关键字调用的函数,属于构造器调用模式,这时this指向这个新对象。
var o = {
value : 0,
ConstructorFunction: function(value){
this.value = value;
return this.value;
}
}
var b = new o.ConstructorFunction(2);
console.log(o.value); //=>0
console.log(b.value); //=>2
这个对于学习过Java面向对象,其实还是很熟悉的。可以理解为o是一个类,而new出来的是对象,this指向对象。
Apply调用模式
apply(obj,arguments)
obj是对象,arguments是数组。
举例:
var obj = {
value: 0,
add: function(value){
this.value += value;
}
}
var myObj = { value: 1 };
obj.add.apply(myObj,[3]);
console.log(obj.value); //=>0
console.log(myObj.value); //=>4
这里apply方法第一个参数为this指向对象,第二个参数为调用函数的参数(通过数组方式传递)。因为apply方法使this指向了myObj,所以this.value+=value相当于myObject.value+=value。
arguments数组
函数被调用时,会被默认传入arguments参数。函数可以通过访问它来访问参数列表。有了这个参数,多态在JS中就好像不那么需要了,我们可以直接使用arguments来实现多态。
举例:
function myConcat(separator) {
var args = Array.prototype.slice.call(arguments, 1);//slice() 方法可从已有的数组中返回选定的元素。
return args.join(separator);//join() 方法用于把数组中的所有元素放入一个字符串。
}}
// returns "red, orange, blue"
myConcat(", ", "red", "orange", "blue");
// returns "sage. basil. oregano. pepper. parsley"
myConcat(". ", "sage", "basil", "oregano", "pepper", "parsley");
异常
var add = function(a,b){
if(typeof a !=='number' || typeof b !== 'number'){
throw{
name:'TypeError',
'message':'add needs number'
}
}
return a+b;
}
使用:
try{
add(1,'two');
}catch(e){
alert(e.name+":"+e.message);//TypeError:add needs number
}
扩充类型的功能
Function基本对象
Function 构造器会创建一个新的 Function 对象。 在 JavaScript 中每个函数都是一个Function对象。
// 创建了一个能返回两个参数和的函数
var adder = new Function("a", "b", "return a + b");
// 调用函数
adder(2, 6);
// 8
Function和Object(知乎)
先有的Object.prototype, Object.prototype构造出Function.prototype,然后Function.prototype构造出Object和Function。
Object.prototype是鸡,Object和Function都是蛋。
下面这张图和实际执行过程并不完全一样,但有助于理解Function和Object的关系。
挖坑:
_proto_(隐式原型)与prototype(显式原型)
- 显式原型的作用:用来实现基于原型的继承与属性的共享。
- 隐式原型的作用:构成原型链,同样用于实现基于原型的继承。举个例子,当我们访问obj这个对象中的x属性时,如果在obj中找不到,那么就会沿着proto依次查找。
总结:
- 对象有属性proto,指向该对象的构造函数的原型对象。
- 方法除了有属性proto,还有属性prototype,prototype指向该方法的原型对象。
通过Function扩充功能
可以通过给Object.prototype添加方法,让该方法对所有对象可用,这样的方式对函数、数组、字符串、数字、正则表达式和布尔值同样适用。
可以通过对Functiong.prototype增加方法使得该方法对所有函数可用。
Function.prototype.method = function(name,func){
this.prototype[name] = func;
return this;
}
使用:
Number.method('integer',function(){
return Math[this<0?'ceil':'floor'](this)
})
(123/20).integer() //-->6