前提:
目前的编程语言分为编译型语言和解释行语言。
javascript是解释型语言,这种脚本语言理论上是根本不需要手动声明一个函数(对象)的,解释器会在执行前把整个脚本加载到内存中,抓取其中的声明部分,然后进行处理对应的函数流程,然后再从头开始执行。这一过程称为解释。解释器帮你做了许许多多的事情,比如申请内存,释放内存,垃圾回收,线程同步,区域通信,变量管理等。
正文:
在实际情况下,如果我们不是要直接使用一个函数中的逻辑功能,而是将这个函数当成类似于java中的类,那么这个函数就“相当于”是一个构造函数了。在表现形式上,构造函数与普通函数没有差别,但是为了规范,一般将普通函数以小写字母开头,而构造函数则以大写字母开头。
构造函数都应该以 一个大写字母开头,eg:
function Person(){...}
而非构造函数则应该以一个小写字母开头,eg:
function person(){...}
上面这2个都是函数对象,用函数对象能创建出普通对象(分配内存,学过java的都很好懂)
构造函数生成实例的时候,每个实例都会自带一个constructor属性 指向该构造函数
所以
var p = new Person();
p.constructor === Person; //true
同时 Javascript规定,每一个构造函数都有一个prototype
属性,指向另一个对象(原型对象)。这个对象的所有属性和方法,都会被构造函数的实例继承。所以可以将一些不变的属性或者方法,直接定义在原型对象上,如果不这样做,每生成一个实例这部分都会在内存中被创建一次。
function Person(name){ this.name = name; } Person.sex = '男'; Person.prototype.age = '21'; var p = new Person('mori'); alert(Person.age);//21 alert(Person.sex);//undefine 也就是说明实例只能继承构造函数原型上的属性和方法
另外,JS在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做__proto__的内置属性,用于指向创建它的函数对象的原型对象prototype。
prototype对象也有__proto__属性,它指向创建它的函数对象(Object)的prototype。
Object.prototype对象也有__proto__属性,但它比较特殊,为null。
因为每个对象都有proto 属性,当我们访问一个对象属性时,如果这个对象不存在就去proto 指向的对象里面找,一层一层找下去,这就是javascript原型链的概念。
p.__proto__ ==> Person.prototype ==> Person.prototype.__proto__ ==> Object.prototype ==> Object.prototype.__proto__ ==> null
因此:
function Person(name){ this.name = name; } Person.prototype.type = "人类"; Person.prototype.say = function(){alert("HI")};
这时所有实例的type
属性和say()
方法,其实都是同一个内存地址,指向prototype
对象,因此就提高了运行效率。