Java ,JS 都是面向对象编程语言,但是它们的语法有重大区别。
Java 中有关键字 class ,用于定义类型。
JS 中没有定义类型的关键字,而是使用函数作为类,准确的说JS是基于原型的语言。
// 相当于构造方法的函数 function makePerson(first, last) { return { first: first, last: last }; } // 相当于功能方法 function personFullName(person) { return person.first + ' ' + person.last; } // 相当于功能方法 function personFullNameReversed(person) { return person.last + ', ' + person.first; } //相当于创建对象 var s = makePerson('Simon', 'Willison'); //相当于调用功能方法 personFullName(s); // "Simon Willison" //相当于调用功能方法 personFullNameReversed(s); // "Willison, Simon"
这种方式定义了太多函数,太丑陋了。
因为JS中函数也是对象,所以可以将函数附加到对象中。
function makePerson(first, last) { return { first: first, last: last, //功能方法 fullName: function() { return this.first + ' ' + this.last; }, //功能方法 fullNameReversed: function() { return this.last + ', ' + this.first; } }; } var s = makePerson('Simon', 'Willison'); //调用功能方法 s.fullName(); // "Simon Willison" s.fullNameReversed(); // "Willison, Simon"
注意this关键字,指向当前对象。
如果使用一个对象 obj 的 . 或 [] 操作符调用该函数,this 就是 obj 。
如果没有用 . 或 [] 调用该函数, this 就是 global。
var s = makePerson('Simon', 'Willison'); var fullName = s.fullName; fullName(); // undefined undefined 此处 this是 global,而global没有 first和last变量。
掌握 this 的使用,可以更进一步优化代码:
function Person(first, last) { this.first = first; this.last = last; this.fullName = function() { return this.first + ' ' + this.last; }; this.fullNameReversed = function() { return this.last + ', ' + this.first; }; } var s = new Person('Simon', 'Willison');
这种方式也存在问题:
创建每一个实例对象时,都会创建 fullName 和 fullNameReversed 函数对象,创建太多的函数对象,造成空间浪费。
function personFullName() { return this.first + ' ' + this.last; } function personFullNameReversed() { return this.last + ', ' + this.first; } function Person(first, last) { this.first = first; this.last = last; this.fullName = personFullName; this.fullNameReversed = personFullNameReversed; }
功能函数只创建了一次,创建实例对象时,没有重新创建功能函数,只是都引用了创建好的函数,节省了空间。
使用原型 prototype
function Person(first, last) { this.first = first; this.last = last; } Person.prototype.fullName = function() { return this.first + ' ' + this.last; }; Person.prototype.fullNameReversed = function() { return this.last + ', ' + this.first; };
Person.prototype 叫做 Person 的原型,它也是一个对象,共享给Person的所有实例使用。
prototype 组成了一个查询链,叫 “原型链”:
任意时刻访问Person的一个不存在属性时,JS都会去检查 Person.prototype 中有没有这个属性。
注意:分配给 Person.prototype 的数据将共享给它的所有实例对象 。
prototype 是一个非常强大的特性,可以在任何时候修改 prototype ,运行时给已有对象添加额外方法。
var s = new Person('Simon', 'Willison'); s.firstNameCaps(); // TypeError on line 1: s.firstNameCaps is not a function Person.prototype.firstNameCaps = function() { return this.first.toUpperCase(); }; s.firstNameCaps(); // "SIMON"
神奇的是,JS的内建对象也可以通过 prototype 添加额外数据。
var s = 'Simon'; s.reversed(); // TypeError on line 1: s.reversed is not a function String.prototype.reversed = function() { var r = ''; for (var i = this.length - 1; i >= 0; i--) { r += this[i]; } return r; }; s.reversed(); // nomiS
reversed 函数对String字面量也可用了
'This can now be reversed'.reversed(); // desrever eb won nac sihT
prototype 原型模式构成了原型链,原型链的顶点是 Object.prototype。
比如说 Object.prototype 中拥有 toString() 函数,当获取对象表达式时会调用 toString() 函数。
var s = new Person('Simon', 'Willison'); s.toString(); // [object Object] Person.prototype.toString = function() { return '<Person: ' + this.fullName() + '>'; } s.toString(); // "<Person: Simon Willison>"
// func 是一个函数对象 // obj 将会作为 this 使用 // args 是调用 func 时传入的参数 func.apply(obj,args)
apply 的用法有点像 java 的反射,对象.方法 -> 方法.对象 。
// trivialNew用来模拟 new,但它不构造原型链,不能代替 new function trivialNew(constructor, ...args) { var o = {}; // Create an object constructor.apply(o, args); return o; } var bill = trivialNew(Person, 'William', 'Orange'); // 等价于 var bill = new Person('William', 'Orange');
apply 与 call 是一个姐妹函数,call 函数将展开表达式替换成了一个数组
// func 是一个函数对象 // obj 将会作为 this 使用 // argsArr 是调用 func 时传入的参数数组 func.call(obj,argsArr)
function lastNameCaps() { return this.last.toUpperCase(); } var s = new Person('Simon', 'Willison'); lastNameCaps.call(s); // Is the same as: s.lastNameCaps = lastNameCaps; s.lastNameCaps(); // WILLISON
new 与 this 有很强的联系。
// 1、new 先创建了一个空对象 {} // 2、调用 Person()函数,this 设置给 创建的 {} 对象 // 3、Person()函数不返回值,作用仅仅是对 this 即 {} 进行修改 // 4、new 关键字返回 this 引用 var s = new Person('Simon', 'Willison');
用 new 关键字调用的函数叫做构造函数,构造函数通常首字母大写,用来提示人们使用 new 来调用。