第一章:面向对象的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 并不等价的,它属于某个对象实例的属性,而后者是属于构造函数的。