js中,方法定义在构造函数内部,外部以及定义在prototype上的区别。

有时候,我们调用某个对象的某个方法时,会报错:xxx is not function,xxx并不是一个函数,说明这个Object里面不存在这个方法,那么,为什么会没有这个方法,可能是这个Object内部没有定义这个方法,可能是这个函数的原型链上不存在该方法等等,让我想探究一下,方法定义在不同位置,对js函数的区别。

我们先来以下案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

function Fn(num){
this.innerAttr = num;
this.innerFn = function(){
console.log(num)
}
};

Fn.wrapFn1 = function(num1){
console.log(num1);
};

Fn.prototype.wrapFn2 = function(num2){
console.log(num2);
};

var fn = new Fn(12);
fn.innerFn(); //12
fn.wrapFn1(14); // is not function
fn.wrapFn2(16); // 16

Fn.innerFn(22); // is not function
Fn.wrapFn1(24); // 24
Fn.wrapFn2(26); // is not function

我们可以把以上调用方式分为两类,一类是实例化的调用,一类是构造函数调用。

我们可以总结出以下区别:

  1. 当方法定义在构造函数内部时,想要调用该方法,只能通过实例化后调用,直接调用会报错xxx is not function

  2. 当方法定义在类(构造函数)时,想要调用该方法,只能通过构造函数调用,其他方式也会报同样错误。

  3. 当方法定义在构造函数的prototype上时,只能通过实例化后调用,其他方式也会报同样错误。

当使用new关键字操作new Fn(12);时,来看看js做了哪些操作:

  1. 创建空对象;
      var obj = {};

  2. 完成原型链的构建

设置新对象的constructor属性为构造函数的名称,设置新对象的__proto__属性指向构造函数的prototype对象;
  obj.proto = Fn.prototype;

  1. 改变this的指向

    使用新对象调用函数,函数中的this被指向新实例对象:
    Fn.call(obj);  //{}.构造函数();

  2. 执行函数里的代码

  3. 将初始化完毕的新对象地址,保存到等号左边的变量中

当我们使用实例化后调用方法时,this指向调用时的实例对象,而这个实例对象继承了构造函数的属性和方法,同时,它的__proto__也指向构造函数的原型,这里解释了1和3中的情况;而2中的情况,通过直接定义在构造函数内部的属性属于私有属性,外部是不能引用的,所以只能通过该构造函数调用,我们可以通过hasOwnProperty()方法判断是否是私有属性。

1、把方法写在原型中比写在构造函数中消耗的内存更小,因为在内存中一个类的原型只有一个,写在原型中的行为可以被所有实例共享,实例化的时候并不会再实例中复制一份。而写在类中的方法,实例化的时候会在每一个实例对象中在复制一份,所以消耗的内存更高。因此没有特殊原因,我们一般把属性定义在类中,行为定义在原型中。
2、在构造函数中定义的属性和方法要比原型中定义的属性和方法优先级高,如果定义了同名称的属性和方法,构造函数中的将会覆盖原型中的。