你的位置:首页 > Java教程

[Java教程]《Javascript面向对象精要》笔记


刚读过《Javascript面向对象精要》这本书,在现有的知识体系里面有一些新鲜的认识,记录一下。
 

原始类型和引用类型

Javascript存在两种类型:原始类型和引用类型。原始类型包括String、Number、Boolean、Null、Undefined,引用类型保存对象,其本质是对象所在内存位置的引用。
原始类型的赋值或者给函数传参,实际上都是传递原始类型值的拷贝;
引用类型则是引用的拷贝。修改其中一个引用的话,其他引用也会受到影响。如果对象中的某个属性也是对象,在对象拷贝时就会引入深拷贝和浅拷贝的区别。
var a = {"name": "Hello"};var b = a;a.name; // "Hello"b.name = "World";a.name; // "World"

关于正则表达式的字面形式

     Javascript允许使用字面形式定义正则表达式,与RegExp构造函数的区别在于,构造函数的参数涉及到转义。
var reg = /\d+/g;//等价于var reg = new RegExp("\\d+", "g");


关于原始封装类型

     String、Number和Boolean有原始封装类型,读取这些类型的值时,自动创建临时的原始封装类型的对象,使用后立即删除。
   引用一个较为诡异的问题:
var name = "Nicholas";name.last = "Zakas";console.log(name.last); // undefined

     这段代码实际的执行过程如下:

var name = "Nicholas";var temp = new String(name);temp.last = "Zakas";temp = null;var temp = new String(name);console.log(temp.last); // undefinedtemp = null;

   所以在获取时,读取的都是临时变量,用后即焚。

 


关于函数重载

     面向对象编程的一个重要特性就是函数重载。Javascript中的函数没有原型的概念,就更提不到重载。
function showMessage(message) { console.log("Parameter: " + message);  }function showMessage() { console.log("No Parameter");}showMessage("Hello"); // "No Parameter"

    利用原始封装类型的概念帮助理解执行过程如下:

var showMessage = new Function("message", "console.log(\"Parameter: \" + message)");showMessage = new Function("console.log(\"No Parameter\")");showMessage("Hello"); // "No Parameter"

    重载的本质在于参数列表的不同,可以通过判断参数的个数、类型等执行不同的处理过程,实现重载。


     1)arguments对象——类似数组类型的对象,可循环遍历,可检查其length属性;
  注意:arguments其实不是数组类型的对象,Array.isArray(arguments)永远返回false;使用arguments的弊端在于如果代码复杂或很长,容易漏掉代码中对于参数的处理。
     2)鉴于以上提到的弊端,实际上检查命名参数是否未定义,或通过typeof/instanceof检查其类型的方法更常见
 

关于继承

     Javascript通过原型链实现继承,每个对象都有一个prototype对象作为其原型对象,继承其属性和方法。for-in循环会遍历继承而来的属性,可通过hasOwnProperty检查是否为自有属性。
     修改原型对象时,会导致所有的对象实例都受到影响,并且影响是实时的;通过构造函数继承的办法,可以实现继承自同一原型对象,但构造过程不同的对象实例的初始化。
function Rectangle(length, width) { this.length = length; this.width = width;}Rectangle.prototype.getArea = function() { return this.length * this.width;}Rectangle.prototype.toString = function() { return "[Rectangle " + this.length + " x " + this.width + "]";}function Square(size) { this.length = size; this.width = size;}Square.prototype = Object.create(Rectangle.prototype, { constructor: {  configurable: true,  enumerable: true,  value: Square,  writable: true }};Square.prototype.toString = function() { return "[Square " + this.length + " x " + this.width + "]";}var rect = new Rectangle(5, 10);var square = new Square(6);console.log(rect.getArea()); // 50console.log(square.getArea()); // 36console.log(rect.toString()); // "[Rectangle 5x10]"console.log(square.toString()); // "[Square 6x6]"console.log(rect instanceof Rectangle); // trueconsole.log(rect instanceof Object); // trueconsole.log(square instanceof Square); // trueconsole.log(square instanceof Rectangle); // trueconsole.log(square instanceof Object); // true


私有成员

     Javascript对象的属性都是public的,通过闭包实现属性的私有化。
     1)模块模式
   person对象被赋值为一个立即执行的函数,该函数返回一个包含name属性和getAge、growOlder方法的对象。由于Javascript的函数作用域限制,函数内的变量是无法被函数以外的上下文访问的,这就保证了只有函数内部可以访问age变量。实际上就是利用了闭包。
var person = (function(){ var age = 25; return {  name: "Nicholas",  getAge: function() {   return age;  },  growOlder: function() {   age++;  } };}());console.log(person.name); // "Nicholas"console.log(person.getAge()); // 25person.age = 100;console.log(person.getAge()); // 25person.growOlder();console.log(person.getAge()); // 26


     2)构造函数的私有成员
function Person(name){ var age = 25; this.name = name; this.getAge = function() {  return age; }; this.growOlder = function() {  age++; };}var person = new Person("Nicholas");console.log(person.name); // "Nicholas"console.log(person.getAge()); // 25person.age = 100;console.log(person.getAge()); // 25person.growOlder();console.log(person.getAge()); // 26


     3)以上二者结合
     构造函数的私有成员示例中,虽然实现了成员的私有化,但是getAge和growOlder方法被对象实例独有,如果需要所有实例共享私有数据,可通过结合模块模式和构造函数来实现。
var Person = (function(){ var age = 25; function realPerson(name) {  this.name = name; } realPerson.prototype.getAge = function() {  return age; } realPerson.prototype.growOlder = function() {  age++; } return realPerson;}());var Nicholas = new Person("Nicholas");var Greg   = new Person("Greg");console.log(Nicholas.name); // "Nicholas"console.log(Nicholas.getAge()); // 25console.log(Greg.name); // "Nicholas"console.log(Greg.getAge()); // 25Nicholas.growOlder();console.log(Nicholas.getAge()); // 26console.log(Greg.getAge()); // 26


作用域安全的构造函数

     这个问题的出现来自于创建对象实例时,很容易漏写new关键字。构造函数也是函数,所以可以不用new操作符直接调用它们来改变this的值。在非严格模式下,this被强制指向全局对象;在严格模式下,构造函数抛出错误。解决这个问题,可在构造函数中进行检查。

function Person(name) { if(this instanceof Person) {  this.name = name; } else {  return new Person(name); }}