JavaScript面向对象编程指南--读书笔记(上)

第一章:面向对象的JavaScript

面向对象的程序设计

面向对象程序设计(oop)中最常用到的概念:

  • 对象、方法、属性
  • 封装
  • 聚合
  • 重用与继承
  • 多态

封装通常有两部分组成。

  • 相关的数据(用于存储数据)
  • 基于这些数据所能做的事情(多能调用的方法)

通过继承可以实现代码的复用。

第二章:基本数据类型、数组、循环及条件表达式

基本数据类型

  • 数字 ——包括浮点数与整数,列如这些属于数字,1、100、3.14;
  • 字符窜 ——包括由任意数量字符组成的序列,例如:“a”、“one”、“one 2 three”;
  • 布尔值 ——包括true和false;
  • undefined ——不存在的变量,或者声明未初始化的变量,值只有一个:undefined;
  • null ——通常指没有或者是空值。

JavaScript数据类型主要分为两个部分:

  • 基本数据类型;
  • 非基本类型(即对象)

任何不属于基本数据类型的东西都属于对象。
可以使用查看类型操作符——typeof 查看某个变量或者值的类型是什么。

数组

数组是一种数据存储的形式,数组的声明:
var a=[];
var a=[1,2,3];
数组的索引下标是从0开始的,并且按照每一个元素的位置依次递增
可通过方括号中的索引值访问数组元素,可存储任何类型的数据,包括另一个数组。

条件与循环表达式

  • if条件表达式
  • switch语句
  • while、do-while、for,及for-in循环

第三章:函数

什么是函数

function sum(a, b) {
    var c=a+b;
    return c;
} 

一般来说,函数声明通常是由下面几部分组成的:
1.关键词function;
2.函数名称,这里即sum;
3.函数需要的参数;
4.函数所要执行的代码块,称为函数体;
5.return 子句。函数通常有返回值,且只有一个,如要返回多个,可以可考虑放进一个数组。

arguments:函数内建变量,接收的所有参数,结构类似数组,可通过索引进行运用。

预定义函数

  • parseInt(); // 接收任何输入值转换为整数型输出 ,记得定义第二个参数确定输出的是什么进制的数值,默认是十进制,例如,从日历中读日期,对于08这样的数值,如果不定义,会出现意想不到的结果。
  • paseFlat( ); // 功能和parseInt( )差不多,它只支持将输入值转为十进制,因此该函数只有一个参数;可接受指数形式的数据,这一点和parseInt()不同。
  • isNaN( );
  • isFinite( );
  • encodeURI( );
  • decodeURI( );
  • ...

函数的作用域

没有块作用域,只有函数作用域,“全局变量”指的是所有函数外的变量,与之对应的“局部变量”是指在某个函数中定义的变量。函数内的代码可以访问全局变量,反之不行。

  var a=3;
  function text(){
    alert(a);
    var a=5;
    alert(a);
  }
text();

上面的例子,第一个alert 结果是undefined,第二个则为5;

函数也是数据

在JavaScript中,函数也是数据,也就是说我们可以把函数赋值给一个变量,

  var f=function(){
      return 1;
  }

上面的这种定义方式也称为函数标识法。
function(){return 1} 是一种函数表达式,其可以被命名,称为命名函数表达式,所以以下是合法的,

 var f = function myFunc(){
    return 1;
  }

函数声明和函数命名函数表达式的差别表现在于他们的上下文,函数声明只出现在程序代码里(在另一个函数体中,或者在主程序中)

匿名函数和回调函数

  var f=function(a){
    return a;  
  }

匿名函数即没有名字的函数,特别是它不被赋值给变量,单独使用的时候,通过匿名函数可以做些什么呢:

  • 可将匿名函数作为参数传给其他函数,,这样,接收函数就能利用我们传递的函数来完成某些事
  • 可以定义某些匿名函数进行某些一次性的任务
 function byTwo(a,b,c,callback){
  var arr=[];
  for(var i=0;i<3;i++){
    arr[i]=callback(arguments[i]*2);
  }
  return arr;
}
byTwo(1,2,3,function(a){ return a+1 }) //  输出 [3,5,7]

以上例子为匿名函数在回调函数中的用处,

(function(){
    alert('box');
})()

匿名函数在即时(自调)函数中作用,其最大的好处是不会产生任何的全局变量,其无法重复执行,所以适合执行一些一次性的或初始化的任务。

闭包

讨论闭包需要了解作用域的概念,
我们知道,JavaScript不存在大括号级的作用域,但它有函数作用域,也就是说函数内定义的所有变量在该函数外是不可见的,而在函数内可以访问的变量既来自它自身的作用域,可以来自其“父级”作用域,这样就形成了一条作用域链,该链的长度(深度)取决于我们的需要。

闭包#1

var a="global variable";
var F=function(){
  var b="local variable";
  var N=function(){
    var c="inner local"
    return b;
  };
return N;
}
console.log(b)  //  undefined
var inner= F();
inner(); // local variable

函数F中包含局部变量b,因此后者在全局空间里是看不到的,N有自己的私有空间,但同时可以访问F()的空间和全局空间,所以b对于来它是可见得。因为F()是可以在全局空间被调用的,所以我们将它的返回值赋于另一个全局变量,从而生成可以访问F()私有空间的全局函数。

闭包#2

function F(){
  var arr=[],
  for(var i=0;i<3;i++){
    arr[i]=function(){
      return i;
    }
  }
  return arr;
}
F() // 输出 [3,3,3];
function F(){
  var arr=[],
  for(var i=0;i<3;i++){
    arr[i]=(function(x){
      return x;
    })(i)
  }
  return arr;
}
F() // 输出 [3,3,3];

第四章:对象

这一章主要讲的是:

  • 如何创建并使用对象;
  • 什么是构造器对象;
  • JavaScript中的内置对象及运用;

事实上对象和数组的情况很相似,不同的是键值类型是自定义的,索引方式不再是数字,而是一些更友好的键名。

 var hero={
  breed: 'Turtle',
  occupation: 'Ninja'
}

正如所见:

  • 一个用于表示该对象名的hero;
  • 与定义数组时所用的中括号[ ]不同的是使用{ };
  • 中括号分割开的是组成该对象的元素(通常称为属性);
  • 键/值之间使用冒号隔开

对象的属性可以有函数,称之方法,

 var hero={
  breed: 'Turtle',
  occupation: 'Ninja',
  getbreed: function(){
        return this.breed;
    }
}
// 可通过两种方式访问对象的属性
hero.breed; // 方式一
hero['breed']; // 方式二
// 调用对象的方法和其他函数的方式相同,在指定的函数名后面加一对括号即可:
hero.getbreed() ; 
hero['getbreed']();

构造器函数

我们可以通过构造器函数的方式来创建对象,

  function Hero(){
    this.occupation='Ninja';
  }

  var hero=new Hero();
  hero.occupation   // 'Ninja'

使用构造器创建对象,可以进行传参,可以利用同一个构造器创建不同的对象。
构造器属性
当创建对象时,同时也赋予该 对象一个特殊的属性---即构造函数属性(constructor property) ,该属性实际上是一个指向用于创建该对象的构造器函数的引用。

hero.constructor //   "function Hero(){  this.occupation='Ninja';}"
typeof(hero.constructor) // "function"

instanceof操作符
通过instanceof操作符可以测试一个对象是不是由某个构造器函数所创建的。

hero.instanceof.Hero // true

返回对象的函数
不使用new操作符调用构造函数创建对象以外,抛开new操作符,只用一般的函数来创建对象。

function factory(name){
    return {
        name:name
    }
}
var o = faactory('jay');
o.name // 'jay'

传递对象和比较对象
对象的拷贝和传递,都是对象的引用,在引用上所做的任何改动,实际上都会影响它所引用的原对象。

var a = { num: 1 };
var b = a;
b.num; // 1
b.num = 2;
a.num; // 2
var a = { num: 1 };
var b = { num: 1 };
a === b; // fasle
a == b; // fasle
var c = a; 
c === a; // true

内建对象

  • Object
  • Array
  • Function
  • Boolean
  • Number

第五章:原型

本章涉及的话题

  • 介绍每个函数都拥有的prototype属性,而该属性所存储的就是原型对象
  • 如何为原型对象添加属性
  • 如何使用原型对象中的新增属性
  • proto介绍,该属性用于保存各对象原型的神秘链接
  • 原型方法介绍,包括isProtottypeOf()、hasOwnProprety()、propertyIsEnumerable()等
 function foo(a,b){
  return a*b;
}
foo.length; // 2
foo.constructor; // function foo(a,b){ return a*b; }
typeof foo.prototype; // object
foo.prototype={ }; //  可以向添加属性和方法,对 foo不会有影响,当foo为构造函数时,其才会起作用

对于原型来说,最重要的一点是理解它的‘实时性’,在JavaScript中,几乎所有的对象都是靠传递引用的方式来进行传递的,所以我们所创建的每一个对象实例并没有属于自己的原型副本,可随时修改prototype属性,由同一构造器所创建的所有对象的prototype属性也会一起改变。

自身属性和原型属性
当我们访问对象实例的某个属性时,JavaScript会遍历所有该对象所有属性,如果找到该属性后立即返回,如果没有找到,脚本引擎会继续去查询用于创建当前对象的构造器的原型。如果在原型中找到该实例会立即使用该属性。

function Hero(){
 this.name = 'java';
this.sex = 'man'
}
Hero.prototype.info='it';
Hero.prototype.sex ='woman';
var hero = new Hero();
hero.name ; // 'java'
hero.info ; // 'it'
hero.sex; // 'man'
hero.hasOwnProperty('sex') //  true;
hero.hasOwnProperty('info') //  false;

当自身属性和原型属性同名时,自身的属性优先级高于原型属性,使用
hasOwnProperty方法可以判断一个对象的属性时自身属性还是原型属性。

枚举属性
for适合使用在数组上,for-in更适合对象,如果想获得某个对象的所有属性的类列表,可以使用for-in。

 var params={
  productid: 666,
  section: 'products'
}
var url = 'https://example.org/page.php?',
      i,
      quert = [];

for( i in params){
  quert.push(i + '=' + params[i]);
}
url += quert.join('&') // url = 'https://example.org/page.php?productid=666§ion=products',

需要注意的细节:

  • 并不是所有的属性 for- in 循环都可以显示的,例如(数组) length、construction 属性就不会被显示,那些会显示的属性称为可枚举的,可通过propertyIsEnumerable方法来判断对象的某个属性是否可枚举。
  • 原型链中的各个原型属性也会被显示出来,当然前提是他们是可枚举的。
  • 对于所有的原型属性, propertyIsEnumerable 都会返回false
function Fadget(name,color){
  this.name = name;
  this.color = color;
  this.getName = function(){
    return this.name;
  }
}
Fadget.prototype.price = 10;
Fadget.prototype.rating = 3;

var newtoy = new Fadget('webcam','black');
for( var prop in newtoy){
  console.log(prop + '=' + newtoy[prop ])
}
// name = name
// name = name
// getName = function(){
    return this.name;
  }
// price  = 10
// rating = 3

// 使用hasOwnProperty对对象属性和原型属性做一个区分
newtoy .hasOwnProperty('name '); // true
newtoy .hasOwnProperty('price '); // false

for(var porp in newtoy ){
  if(newtoy.hasOwnProperty(porp)) {
    console.log(porp + '=' +newtoy[porp] );
  }
}
// name = name
// name = name
// getName = function(){
    return this.name;
  }

//propertyIsEnumerable,该方法会对所有的非内建对象属性返回true
//而对于内建对象和方法来说,大部分都是不可枚举的,
newtoy.propertyIsEnumerable('name ')  // true
newtoy.propertyIsEnumerable('constructor ')  // false

//任何原型链上的属性也是不可枚举的
newtoy.propertyIsEnumerable('price ')  // false

// 但需要注意,如果propertyIsEnumerable()调用的是来自原型链上的某个对象,那么该对象中的属性也是枚举的
newtoy.constructor .prototype.propertyIsEnumerable('price ')  // true

每个对象中都有isPrototypeOf()方法,这个方法会告诉我们当前对象是否是另一个对象的原型。
proto IE 之类平台不存在,所以使用上存在不能夸平台,还有和prototype 并不等价的,它属于某个对象实例的属性,而后者是属于构造函数的。

你可能感兴趣的:(JavaScript面向对象编程指南--读书笔记(上))