第1-2章 JavaScript 简介
- <script> tag有6个属性,其中,
async表示应该立即下载脚本,但不应该妨碍页面中其他操作,其目的是不让页面等待两个脚本下载和运行,从而异步加载页面其他内容 - <script>标签的
defer属性,表明脚本在执行时不会影响页面的构造,脚本会被延迟到整个页面都解析完毕后再运行,浏览器立即下载脚本,但延迟运行 - defer会按照脚本顺序执行,async不一定
- <script>按照先后顺序依次被解析
- <noscript>用来显示不支持脚本的的浏览器的代替内容
第3章 基本概念
3.1 严格模式
"use strict";
var messege; //未经初始化,值为undefind
3.2 数据类型(共6种)
基本数据类型:Undefined, Null, Boolean, Number, String
复杂数据类型:Object(本质是无序的名值对)
//typeof:经常用来检测一个变量是不是最基本的数据类型
var a;
typeof a; // undefined
a = null;
typeof a; // object
a = true;
typeof a; // boolean
a = 666;
typeof a; // number
a = "hello";
typeof a; // string
a = Symbol();
typeof a; // symbol
a = function(){}
typeof a; // function
a = [];
typeof a; // object
a = {};
typeof a; // object
a = /aaa/g;
typeof a; // object
- null值表示一个空对象指针
var car = null;
alert(typeof car); // "object"
undefined值派生于null值,所以alert(null == undefined);返回true
Boolean类型只有两个值,true和false,但true不一定等于1,false也不一定等于0
| 数据类型 | 转换后为true的值 | 转换后为false的值 |
|---|---|---|
| Bollean | true | false |
| string | 非空字符串 | ""(空字符串) |
| Number | 非零数值 | 0和NaN |
var message = "Hello World";
if(message){
aleart("Value is true");
}
string类型的message被自动转化为Boolean类型
- Number类型
float的精度是17位小数,所以精度不如整数,0.1+0.2不等于0.3
最小数值在Number.MIN_VALUE中,最大数值在Number.MAX_VALUE中
NaN和自身不相等
aleart(NaN == NaN); //false
Obeject类型
- Obejct类型是所有它的实例的基础,其所具有的任何属性和方法也同样存在于更具体的对象中
- constructor
- hasOwnProperty()
- isPrototypeOf()
- propertyIsEnumerable
- toLocaleString
- toString()
- valueOf()
3.3 操作符
- 一元操作符不仅适用于整数,还可以用于字符串、Bool、float和对象
3.3.1相等和全等
相等和不相等--先转换再比较
全等和不全等--仅比较而不转换
3.4 语句
for-in 语句是一种精准的迭代语句,可以用来枚举对象的属性
for (var propNmae in window){
console.log(propName); //打印window对象的所有属性
}
3.5 函数
函数不介意传递进来多少个参数或参数的类型。即便定义的函数只接收两个参数,调用时也未必一定要传递两个参数。
ECMAScript中的参数在内部使用一个数组来表示的,汉书接收到的始终都是这个数组,而不关心数组中包含哪些参数(如果有参数的话)
在函数体内可以通过arguments对象来访问这个参数数组,从而获取传递给参数的每一个数组
function sayHi(name, message){
alert("Hello " + name +“, ” + message)
}
与下面代码相同
function sayHi(){
alert("Hello " + arguments[0] +“, ” + arguments[1])
}
函数的一个重要特点:命名的参数只提供便利,但不是必需的
arguments对象可以与命名参数一起使用,它的值永远与对应命名参数的值保持同步。
没有重载
没有重载的原因是因Javascript函数没有函数签名
如果两个函数名字相同,只会调用后定义的函数;虽然如此,但可以通过判断arguments.length来实现类似的重载功能
第4章 变量、作用域和内存问题
4.1 传参
函数的参数是按值传递的,在向参数传递基本类型的值时,被传递的值会被复制给一个局部变量;传递引用类型的时候,会把这个值在内存中的地址复制给一个局部变量
instanceof 用来检测引用类型的对象
4.2 执行环境及作用域
执行环境定义了变量或函数有权访问其他数据,每个执行环境都有一个与之关联的变量对象(variable object),环境中定义的所有变量和函数都保存在这个对象中
每个函数都有自己的执行环境,当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain),其功能,是保证对变量和函数的有序访问。
内部环境可以通过作用域链访问所有的外部环境,但外部环境不能访问内部环境中的任何变量和函数
4.2.2 没有块级的作用域
if或for语句声明的变量会将变量添加到当前的执行环境。
使用var生命的变量会被自动添加到最近的环境中
第5章 引用类型
5.1 Obejct类型
可以使用方括号来访问对象的属性 alert(person["name"]);
5.2 Array 类型
数组的length属性,可以从数组的末尾移除或向数组中添加新项
var colors = ["red", "blue", "green"];
colors[colors.length] = "black"; //(在[3]添加black)
colors[colors.length] = "brown"; //(在[4]添加brown)
if(Array.isArray(value)){
//检测对象是否是数组
}
alert()要接受字符串参数,在后台调用的是toString()方法
- join()方法接受一个参数,用作分隔符的字符串,返回包含所有数组项的字符串
5.2.3 栈方法
栈是后进先出,栈中的push和pop方法只发生在栈的顶部
- push()方法返回修改后的数组的长度
- pop()方法返回移除的项
5.2.4 队列方法
队列是先进先出,因此队列在列表的末端添加项,在列表的前端移除项
- push()在数组末端添加项
- shift()在数组前端移除项,返回该项
5.2.6 数组操作方法
- concat() 返回一个新数组
- slice(1); slice(1,4) 返回从其实和结束位置之间的项,不包括结束的位置,不会影响原始数组
如果slice()方法参数是负数,则用数组长度加上负数来确定位置,比如slice(-2, -1),数组长度为5,则与slice(3, 4)得到的结果相同
- splice() 可作删除、插入、替换
- indexOf() lastIndexOf
5.2.7 数组的迭代方法
- forEach(),对数组每一项进行操作,但没有返回值
- map(),对数组每一项进行操作,返回数组
- filter(),返回函数返回true的项组成的数组
- every(),数组每一项都返回true,则返回true
- some(),数组任一项返回true,则返回true
前三者返回数组,后两个返回true或false
//forEach的操作,forEach()本质上与使用for循环迭代数组一样
var numbers = [1,2,3,4,5,4,3,2,1];
numbers.forEach(function(item, index, array){
//执行操作;
)
//map的操作
var numers = [1,2,3,4,5,4,3,2,1];
var mapResults = numbers.map(function(item, index, array){
return item*2;
})
alert(mapResults); //[2,4,6,8,10,8,6,4,2]
//filter的操作方法
var numbers = [1,2,3,4,5,4,3,2,1];
var filterResult = numbers.filter(function(item, index, array){
return (item>2);
});
alert(filterResult); //[3,4,5,4,3]
5.3 Date类型
var now = new Date();
传入构造函数的是表示日期的毫秒数(从1970年1月1日起到该日期的毫秒数),Date.parse()和Date.UTC()会返回毫秒数
console.log(Date.parse("May 25, 2004"));
//1085457600000
var someDate = new Date(Date.parse("May 25, 2004"));
console.log(someDate);
//Tue May 25 2004 00:00:00 GMT-0400 (Eastern Daylight Time)
使用Date对象分析代码运行时间
//取得开始时间
var start = Date.now();
//调用函数
doSomething();
//取得停止时间
var stop = Date.now();
result = stop - start;
5.4 RegExp 类型
var expression = / pattern / flags;
- g 全局模式
- i 不区分大小写
- m 多行
5.5 Function 类型
函数实际上是对象,每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法。函数名实际上是一个指向函数对象的指针,不会与某个函数绑定。
使用不带圆括号的函数名是访问函数指针,而非调用函数
5.5.1 没有重载(深入理解)
将函数名想象为指针,有助于理解为什么ECMAScript中没有函数重载的概念。
5.5.2 函数声明与表达式
解析器在向执行环境中加载数据时,对函数声明和函数表达式并非一视同仁。解析器会先读取函数声明,并使其在执行任何代码之前可用。至于函数表达式,则必须等到执行器执行到他所在的代码行,才真正会被解释执行。
//以下代码会正常运行
alert(sum(10,10))
function sum(num1, num2){
return num1 + num2;
}
//但如果用var sum = function(num1,num2)就会出错
在代码开始执行前,会先函数声明提升(function declaration hoisting),读取并将函数声明添加到执行环境中。对代码求值时,代码被放到了源代码树的顶部。
5.5.3 作为值的函数
函数名本身就是变量,所以函数也可以作为值来用。不仅可以像传递参数一样把一个函数传给另一个函数,而且可以return 一个函数
5.5.4 函数内部属性this
this引用的是函数执行的环境对象
5.5.5 函数的属性和方法
每个函数都包含两个属性,length和prototype。length表示函数希望接收的命名参数的个数。
对于ECMAScript中的引用类型而言,prototype是保存它们所有实例方法的真正所在。
5.6 基本包装类型
3个特殊的引用类型,Boolean、Number、String
var s1 = "some text";
var s2 = s1.toString();
//基本类型string本身没有toString()方法,
//但后台在会自动完成下面的处理:
//1. 创建String类型的实例;
//2. 在实例上调用指定的方法
//3. 销毁这个实例
使用new创建的引用类型的实例,在执行流离开当前作用域之前一直保存在内存中;而自动创建的基本包装类型对象,只存在于一行代码的执行瞬间,然后立即被销毁。
5.6.3 String 类型
- charAt()
- concat() 拼接字符串,原字符串不变
- slice()、substr()、substring()会返回一个子字符串,原字符串不变
- indexOf()、lastIndexOf()
- trim() 删除前置和后缀所有空格,返回新字符串
- toLowerCase()、toUpperCase()
第6章 面向对象的程序设计
6.1 理解对象
6.2 创建对象
6.2.1 工厂模式
抽象了具体创建对象的过程,但却没有解决对象是别的问题(即怎么样知道一个对象的类型)
function createPerson(name, age, job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
}
return o;
}
var person1 = createPerson("Nick", 29, "Web developer");
var person2 = createPerson("Ryan", 28, "Software engineer");
6.2.2 构造函数模式
创建自定义的构造函数,从而定义自定义的对象类型的属性和方法
//重写了上面的代码,不同之处在于:
//1.没有显示地创建对象
//2.直接将属性和方法赋给了this对象
//3.没有return 语句
//4.使用了new 来创建对象
function Person(name, age job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
aleart(this.name);
};
}
var person1 = new Person("Nick", 29, "Web developer");
var person2 = new Person("Ryan", 28, "Software engineer")
上面的创建对象的方法还有问题,即每次都创建了一个新的function实例化对象,可以重写如下:
//person1和person2的sayName在全局作用域中共享一个方法
function Person(name, age job){
this.name = name;
this.age = age;
this.job = job;
//sayName包含的是指向函数的指针
this.sayName = sayName;
}
//全局函数
function sayName(){
alert(this.name);
}
var person1 = new Person("Nick", 29, "Web developer");
var person2 = new Person("Ryan", 28, "Software engineer")
但上述方法仍有问题,如果对象需要定义很多方法,则需要定义很多全局函数,这个自定义的引用类型就丝毫没有封装性可言了,于是,出现了原型模式。
6.2.3 原型模式
我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。
prototype是通过调用构造函数而创建的那个对象实例的原型对象。使用原型对象的好处是可以让所有对象实例共享它包含的属性和方法。
不必在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中