JavaScript之原型、函数、实例

时间:2021-11-17 14:35:03

JavaScript 函数语法

函数就是包裹在花括号中的代码块,前面使用了关键词 function:

function functionname()
{
     // 执行代码
}

当调用该函数时,会执行函数内的代码。

可以在某事件发生时直接调用函数(比如当用户点击按钮时),并且可由 JavaScript 在任何位置进行调用。

JavaScript之原型、函数、实例 JavaScript 对大小写敏感。关键词 function 必须是小写的,并且必须以与函数名称相同的大小写来调用函数

在调用函数时,您可以向其传递值,这些值被称为参数。

这些参数可以在函数中使用。

您可以发送任意多的参数,由逗号 (,) 分隔:

myFunction( argument1,argument2)

当您声明函数时,请把参数作为变量来声明:

function myFunction( var1, var2)
{
代码
}

变量和参数必须以一致的顺序出现。第一个变量就是第一个被传递的参数的给定的值,以此类推。

在进行函数调用的时候,不管实参的数目大于形参还是小于形参,被调用的函数都会执行;在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;
}

JavaScript之原型、函数、实例

(google浏览器)

如上,当我们创建一个函数,系统就会为这个函数自动分配一个prototype指针,指向它的原型对象。并且可以发现,这个原型对象包含两个部分(constructor 和 __proto__)其中constructor指向函数自身。(这里形成了一个小闭环)

var p = new Person("jack");
console.log(p);

JavaScript之原型、函数、实例

JavaScript之原型、函数、实例

 

我们将该函数作为模版创建实例(new方法)的时候,我们发现创建出的实例是一个与构造函数同名的object,这个object是独立的,也就是这里的p,他只包含了一个__proto__指针(实例没有prototype,强行访问则会输出undefined),这个指针指向上面提到的构造函数的prototype原型对象,则可以得出:p.__proto__===Person.prototype

结果如下:

JavaScript之原型、函数、实例

 在查看下Object的原型对象:

JavaScript之原型、函数、实例

通过上面的代码可以看到:

  • Object对象本身是一个函数对象。
  • 既然是Object函数,就肯定会有prototype属性,所以可以看到"Object.prototype"的值就是"Object {}"这个原型对象。
  • 反过来,当访问"Object.prototype"对象的"constructor"这个属性的时候,就得到了Obejct函数。
  • 另外,当通过"Object.prototype.__proto__"获取Object原型的原型的时候,将会得到"null",也就是说"Object {}"原型对象就是原型链的终点了

在查看下Function的对象:

JavaScript之原型、函数、实例

结果分析 :

  • 在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

JavaScript之原型、函数、实例

 

最后上一个图,便于理解

 JavaScript之原型、函数、实例