JavaScript 函数语法
函数就是包裹在花括号中的代码块,前面使用了关键词 function:
{
// 执行代码
}
当调用该函数时,会执行函数内的代码。
可以在某事件发生时直接调用函数(比如当用户点击按钮时),并且可由 JavaScript 在任何位置进行调用。
JavaScript 对大小写敏感。关键词 function 必须是小写的,并且必须以与函数名称相同的大小写来调用函数 |
在调用函数时,您可以向其传递值,这些值被称为参数。
这些参数可以在函数中使用。
您可以发送任意多的参数,由逗号 (,) 分隔:
当您声明函数时,请把参数作为变量来声明:
{
代码
}
变量和参数必须以一致的顺序出现。第一个变量就是第一个被传递的参数的给定的值,以此类推。
在进行函数调用的时候,不管实参的数目大于形参还是小于形参,被调用的函数都会执行;在JS中函数不介意传递进来多少个参数,也不在乎传进来的参数是什么数据类型。发生函数调用的时候可以给一个实参也可以给多个实参,之所以会这样,是因为在js中的参数在内部是用一个数组来表示。函数接收到的始终是这个数组,而不关心数组中包含哪些参数,如果这个数组不包含任何参数也无所谓,包含多个参数也没问题,在函数体内可以通过arguments(参数)对象来访问这个参数数组,从而获取传递给参数的每个参数。 arguments对象和数组对象相类似,可以通过下标来获取传入的每一个元素(第一个元素是arguments[0]);也可以使用length属性来确定传递进来多少个参数,arguments对象的长度是由传入的参数个数决定,不是由定义函数的参数的个数决定
四种调用函数的方式及this指向
1、函数调用模式,this指向的全局对象window.
实例1:
函数声明
1
2
3
4
5
|
function
add(a,b) {
return
a+b;
}
console.log(add(1,2));
|
实例2:
函数表达式
1
2
3
4
5
|
var
add=
function
(a,b) {
return
a+b;
}
console.log(add(1,2));
|
2、方法调用模式,this指向当前的对象
var
o = {
prop: 21,
f:
function
() {
return
this
.prop;
}
};
console.log(o.f());
3、构造器模式,this指向新创建的实例对象,也就是这里的o
function
MyClass() {
this
.a = 21;
}
var
o =
new
MyClass();
console.log(o.a);
//21
4、apply\call模式,这两个的第一参数即this,当第一个参数为null,this指向window;当第一个参数为一个对象,this就指向当前这个对象。
call和apply的作用一样,只是接受参数的方式不一样,call接受的是多个单个参数,apply接受的是参数数组。
call和apply的作用简单地可以说成,当一个对象实例缺少一个函数/方法时,可以调用其他对象的现成函数/方法,其方式是通过替换其中的this为这个对象实例,改变函数运行时的上下文。call方法的妙用:1、继承 2、改变函数运行时的this指针
例如:
function Dog(){ this.sound="汪汪汪"; } Dog.prototype.bark=function(){ alert(this.sound); }
现在我有另外一个cat对象:
var cat={sound:'喵喵喵'}
我也想让这个cat对象可以调用bark方法,这时候就不用重新为它定义bark方法了,可以用call/apply调用Dog类的bark方法:
Dog.prototype.bark.call(cat);
或者:
dog.bark.call(cat);
加点东西,变成一个带参数的栗子:
function Dog(){ this.sound="汪汪汪"; } Dog.prototype.bark=function(words){ alert(this.sound+" "+words); } var dog=new Dog(); dog.bark("有小偷");//alert:汪汪汪 有小偷 Dog.prototype.bark.call(cat,"饿了");//alert:喵喵喵 饿了
普通函数与构造函数的区别
普通函数与构造函数相比有四个明显特点:
1.不需要用new关键字调用
fn();
2.可以用return语句返回值
function fn(a,b){ return a+b; } alert(fn(2,3));//alert:5
3.函数内部不建议使用this关键字
我们说不建议使用,当然硬要用是可以的,只是要注意这时候发生了什么。如果在普通函数内部使用this关键字定义变量或函数,因为这时候this指向的是window全局对象,这样无意间就会为window添加了一些全局变量或函数。
function greeting(){ this.name="sheila"; alert("hello "+this.name); } greeting();//alert:hello sheila alert(window.name);//alert:sheila
4.函数命名以驼峰方式,首字母小写
与普通函数相比,构造函数有以下明显特点:
1.用new关键字调用
var prince=new Prince("charming",25);
2.函数内部可以使用this关键字
在构造函数内部,this指向的是构造出的新对象。用this定义的变量或函数/方法,就是实例变量或实例函数/方法。需要用实例才能访问到,不能用类型名访问。
prince.age;//25 Prince.age;//undefined
3.默认不用return返回值
构造函数是不需要用return显式返回值的,默认会返回this,也就是新的实例对象。当然,也可以用return语句,返回值会根据return值的类型而有所不同。
构造函数里调用return时,分两种情况:
3.1 return的是五种简单数据类型:String,Number,Boolean,Null,Undefined。
这种情况下,忽视return值,依然返回this对象。
3.2 return的是Object
这种情况下,不再返回this对象,而是返回return语句的返回值。
function Person(name){ this.name=name; return {name:"cherry"} } var person=new Person("sheila"); person.name;//cherry person;//Object {name: "cherry"}
4.函数命名建议首字母大写,与普通函数区分开。
不是命名规范中的,但是建议这么写。
使用new关键字实例化的时候发生了什么?
以上文中的Prince()函数举个栗子:
1.第一步,创建一个空对象。
var prince={}
2.第二步,将构造函数Prince()中的this指向新创建的对象prince。
3.第三步,将prince的_proto_
属性指向Prince函数的prototype,创建对象和原型间关系
4.第四步,执行构造函数Prince()内的代码
原型对象
function Person(name){
this.name = name;
}
(google浏览器)
如上,当我们创建一个函数,系统就会为这个函数自动分配一个prototype指针,指向它的原型对象。并且可以发现,这个原型对象包含两个部分(constructor 和 __proto__)其中constructor指向函数自身。(这里形成了一个小闭环)
var p = new Person("jack");
console.log(p);
我们将该函数作为模版创建实例(new方法)的时候,我们发现创建出的实例是一个与构造函数同名的object,这个object是独立的,也就是这里的p,他只包含了一个__proto__指针(实例没有prototype,强行访问则会输出undefined),这个指针指向上面提到的构造函数的prototype原型对象,则可以得出:p.__proto__===Person.prototype
结果如下:
在查看下Object的原型对象:
通过上面的代码可以看到:
- Object对象本身是一个函数对象。
- 既然是Object函数,就肯定会有prototype属性,所以可以看到"Object.prototype"的值就是"Object {}"这个原型对象。
- 反过来,当访问"Object.prototype"对象的"constructor"这个属性的时候,就得到了Obejct函数。
- 另外,当通过"Object.prototype.__proto__"获取Object原型的原型的时候,将会得到"null",也就是说"Object {}"原型对象就是原型链的终点了
在查看下Function的对象:
结果分析 :
- 在JavaScript中有个Function对象(类似Object),这个对象本身是个函数;所有的函数(包括Function,Object)的原型(__proto__)都是"Function.prototype"。
- Function对象作为一个函数,就会有prototype属性,该属性将对应"function () {}"对象。
- Function对象作为一个对象,就有"__proto__"属性,该属性对应"Function.prototype",也就是说,"Function.__proto__ === Function.prototype"
- 对于Function的原型对象"Function.prototype",该原型对象的"__proto__"属性将对应"Object {}"
在这里对"prototype"和"__proto__"进行简单的介绍:
- 对于所有的对象,都有__proto__属性,这个属性对应该对象的原型
- 对于函数对象,除了__proto__属性之外,还有prototype属性,当一个函数被用作构造函数来创建实例时,该函数的prototype属性值将被作为原型赋值给所有对象实例(也就是设置实例的__proto__属性)。也就是Person.prototype===p.__proto__
1、每个函数都有prototype属性,这个属性指向函数的原型对象
2、所有对象的__proto__都指向其构造器(constructor)的prototype
举个栗子:
function Person(name){
this.name = name;
}
var p = new Person("jack");
console.log(p.__proto__===Person.prototype);//true
console.log(p.__proto__===p.constructor.prototype);//true
3、构造函数、实例和原型对象的区别与联系
3.1 实例就是通过构造函数创建的。实例创造出来就具有constructor属性(指向构造函数)和__proto__属性(指向原型对象)
3.2 构造函数中有一个prototype属性。这个属性是一个指针,指向它的原型对象
3.3 原型对象内部也有一个指针(constructor)指向构造函数。Person.prototype.constructor=Person;
3.4 Person.prototype.__proto__语句获取原型对象的原型,将得到Object对象。所有对象的原型都将追溯到Object对象。
Person.prototype.__proto__ === Object.prototype; //true
最后上一个图,便于理解