文章目录
1. null和undefined
-
null表示一个空的值,undefined表示值未定义。
-
undefined仅仅用在判断函数参数是否传递的情况下使用。
2. 数据类型
-
五种基本数据类型:
Undefined
,Null
,Boolean
,Number
和String
。 -
复杂数据类型:
object
,本质上是由一组无序的名值对组成的。 -
typeof
检测给定变量的数据类型。var message = "some string"; alert(typeof message); // "String"
-
Undefined
类型:该类型只有一个值undefined
。使用var声明变量,但未对其加以初始化, 变量的值即为undefined
。无论什么情况都没有必要把一个变量显式的设置为undefined
.undefined
派生自null
值。alert(null == undefined); // true
-
Null
类型:该类型也只有一个值null
.null
表示一个空对象指针。var car = null; alert(typeof car); // "object"
只要准备用来保存对象的变量没有真正保存对象,就应该明确地将这变量初始化为
null
值。 -
Boolean
类型:只有两个字面值true
和false
,区分大小写。True
和False
只是标识符。Boolean()
转型函数数据类型 true false Boolean true false String 任何非空字符 “”(空字符) Number 任何非零(包括无穷大) 0和NaN Object 任何对象 null Undefined undefined -
浮点数值:小数点后面必须至少有一位数字。
var floatNum1 = 1.; // 解析为1 var floatNum2 = 10.0 // 解析为10
永远不要测试某个特定的浮点数值。如
0.1 + 0.2 = 0.30000000000000004
不等于0.3
. -
NaN
:用于表示本来要返回数值的操作数未返回数值得情况。***`NaN`*** 与其它所有的值都不相等,包括它自己;任何涉及`NaN`都返回`NaN`。
-
String
:null
和undefined
没有toString()
方法。 -
基本数据类型占固定大小,保存在***栈内存***中;引用数据类型保存在***堆内存***中。
3. 一元操作符
-
递加递减:前置型和后置型
-
遵循的规则:
- 应用包含有效数字字符串,先转换为数字值,再执行。
- 不包含有效数字字符串,将变量的值设置为
NaN
,再执行。 false
先转换为0,true
转换为1, 再执行。- 应用于对象时,先调用对象的
valueof()
方法,再执行操作。
var s1 = "2";
var s2 = "z";
var b = false;
var f = 1.1;
var o = {
valueof: function() {
return -1;
}
}
s1++; // 值变为3
s2++; // 值变成NaN
b++; // 值变成1
f--; // 值变为0.10000000000000009
o--; // 值变为-2
4. 比较运算符
- 第一种 ==, 它会自动转换数据类型再比较,会出现诡异的结果。
- 第二种 === , 它不会自动转换数据类型,如果数据类型不一致,返回
false
,如果一直一致,再比较。应该坚持使用。
5. 语句
-
for-in
语句是一种精准迭代语句,用来枚举对象属性。- 语法:
for (property in expression) statement
- 建议在使用循环之前,先检测对象的值不是
null
或undefined
。
- 语法:
-
label
语句:在代码中添加标签- 语法:
label: statement
- 语法:
-
var num = 0; outPoint: for (var i = 0 ; i < 10 ; i++) { for (var j = 0 ; j < 10 ; j++) { if( i == 5 && j == 5 ) { break outPoint; } num++; } } alert(num); // 输出为55
6. 理解函数参数
-
ECMAScript
(或ES
)是由Ecma International
标准化的脚本语言规范。 -
参数在内部是用一个数组来表示,函数体内可以通过
arguments
对象来访问这个参数数组,获取传递的每个参数 。arguments
对象与数组类似(但并不是array
的实例)。 -
function sayHi() { console.log(arguments[0] + arguments[1]); } sayHi(1, 2); // 3
-
通过访问
arguments
对象的length
属性可以获知有多少个参数传递给函数,利用这一点让函数接收任意参数实现相应的功能(类似重载功能)。 -
function doAdd(num1, num2) { if (arguments.length == 1) { console.log(num1 + 10); } else if (arguments.length == 2) { console.log(arguments[0] + num2); // } } doAdd(1); // 11 doAdd(1, 3); // 4
-
没有重载功能,只能通过上述方法实现类似重载功能。
-
非
严格模式
下,修改arguments
会影响对应的参数值,反之不行。内存空间独立,值单向同步。
7.变量
-
变量赋值引用类型的值时,两个变量实际上引用同一个对象,指针指向存储在堆中的一个对象,改变一个会影响另一个。
-
所有函数的参数都是按值传递的。
-
instanceof
监测引用类型的值,判断是什么类型的对象 -
没有块级作用域:
if
语句中的变量声明会将变量添加到当前的执行环境,使用for
要牢记之一差异。for (var i = 0; i < 6; i++) { doSomething(i); } console.log(i); // 6
-
管理内存:一单数据不在有用,最好通过将其值设置为
null
来释放其应用,叫解除引用。
8. 引用类型
-
访问对象属性的方法:点表示法,方括号表示法。(通常采用点表示法)
方括号表示法的优点:属性名中包含会导致语法错误的字符,或者使用的关键字或者保留字,可以使用此方法。
person["first name"] = "nicholas
" -
Array
创建的两种方式:- Array构造函数
var colors = new Array(3);
- 字面量表示法(不会调用Array构造函数)。
var colors = ["red", "blue", "green"]
- Array构造函数
-
Array:
push()
接收任意数量参数,加到数组末尾,返回数组长度。pop()
从数组末尾移除最后一项。shift()
移除数组第一项并返回该项。unshift()
在数组前端添加任意个项并返回数组的长度。reverse()
反转数组项的顺序。sort()
调用每项的toString()
方法,再比较字符串升序排列
9. Function类型
-
函数声明与函数表达式:解析器会率先读取函数声明,将它们放到源代码树的顶部,使其在执行任何代码之前可用;表达式必须等到解析器执行到所在代码行时才会真正被执行。
// TypeError: functionOne is not a function functionOne(); var functionOne = function() { console.log("Hello!"); }; /*-------------------------------------------*/ // Outputs: "Hello!" functionTwo(); function functionTwo() { console.log("Hello!"); }
-
函数内部属性:
arguments
和this
.- arguments:用于保存函数参数,该对象还有一个callee属性(指向拥有对象的函数的指针)。
// 阶乘函数,递归算法,消除紧密耦合 function factorial(num) { if (num == 1) { return 1; } else { return num * arguments.callee(num - 1); } }
this
:引用的是函数当前执行的环境对象(全局作用域调用函数时,this对象引用的window)。- 函数名字仅仅是包含指针的变量而已,不同环境中执行,指向的任然是同一个函数。
caller
保存着调用当前函数的函数的引用。
-
函数的属性和方法:
-
属性:length和prototype。
- length:表示函数希望接收到的命名参数的个数。
- prototype:保存所有实例方法。
-
方法:
apply()
和call()
,用于在特定的作用域中调用函数。-
apply()
:接收两个参数:运行函数的运行域和参数数组。 -
call()
:作用相同,区别在与第二个参数必须逐个列举出来。function sum(num1, num2) { return num1 + num2; } function callSum(num1, num2) { return sum.call(this, num1, num2);// 逐个传入参数 } function applySum(num1, num2) { return sum.apply(this, arguments);// 传入参数数组 }
-
扩充函数的作用域。
window.color = "red"; var o = {color: "blue"}; function sayColor() { console.log(this.color); } sayColor(); // red sayColor(this); // red sayColor(window); // red sayColor(o); // blue
-
-
10. 创建对象
-
构造函数模式:
- 函数名首字母使用大写,构造函数本身也是函数,只不过是用来创建对象的而已。
- 创建新实例,必须使用
new
操作符。调用构造函数会经历一下步骤:- 创建一个新的对象。
- 将构造函数作用域赋给新对象(this指向这个新对象)。
- 执行构造函数代码。
- 返回新对象。
- 每个实例对象都有一个
constructor
(构造函数)属性,该属性指向构造函数。 - 构造函数问题:每个方法都要在实例上重新创建一遍。
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = function(){ console.log(this.name); } }
-
原型模式:每个函数都有一个
prototype
属性,该属性是一个指针,指向一个原型对象。
-
原型对象默认只会取得
constructor
属性,其他方法都是从Object继承而来的。 -
可以通过
isPrototypeOf()
方法确定对象之间是否存在关系。Person.prototype.isPrototypeOf(person1)
-
当代吗读取某个对象的属性时,会执行一次搜索,目标是具有给定名字的属性。首先从对象实例本身开始,如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找属性。
-
当为对象实例添加一个属性时,这个属性只会屏蔽原型对象中保存的同名属性,不会修改那个属性。
使用
delete
可以完全删除实例属性,重新访问原型中的属性。 -
hasOwnProperty()
方法可以检测属性是否存在于实例中还是存在于原型中。 -
in
操作符:通过对象能够访问给定属性时返回true,无论该属性存在于实例还是原型中。 -
简单的原型语法:例如
Person.prototype = { constructor: Person,// 设置constructor值 name: "Ni", sayName: function() { console.log(this.name); } }
这样写的问题是重写了
prototype
,导致constructor
属性变成了新的对象的属性,无法通过constructor
确定对象类型。如果constructor
很重要,可以加入一行设置constructor
值。实例中的指针仅指向原始原型对象,不指向构造函数。 -
-
组合使用构造函数模式和原型模式
- 构造函数模式用于定义实力属性,而原型模式定义方法和共享的属性。
- 使用最广泛,认同度最高。
11. 闭包
-
闭包:指有权访问***另一个函数作用域***中的变量的函数。
-
在创建函数时,会预先创建包含全局变量对象的作用域链,被保存在内部的
scope
属性中,调用函数时会创建执一个执行环境并复制scope
属性中的对象从而构建起作用域链。 -
在另一个函数内部定义的函数会将包含函数的活动对象添加到它的作用域中。函数返回匿名函数后,执行环境的作用链会被销毁,但活动对象仍然留在内存中,知道匿名函数销毁后。
-
闭包与变量:闭包只能取得包含函数中任何变量的最后一个值。
-
this
对象:是运行时基于函数的执行环境绑定的。 -
私有变量:
- 有权访问私有变量和私有函数的公有方法称为特权方法。
function MyObject() { // 私有变量和私有函数 var privateVariable = 10; function privateFunction() { return false; } // 特权方法 this.publicMethod = function () { privateVariable++; return privateVariable; } }
- 静态私有变量
(function() { // 私有变量和私有函数 var privateVariable = 10; function privateFunction() { return false; } // 构造函数 MyObject = function () {// MyObject 为全局变量 }; MyObject.prototype.publicMethod = function() { privateVariable++; return privateVariable; } })(); // 与在构造函数中定义特权方法的主要区别为:私有变量和函数是由实例共享的。
12. 继承
-
原型链(很少单独使用)
- 构造函数、原型对象和实例的关系:每个构造函数有一个原型对象,原型对象包含一个指向构造函数的指针,实例都包含一个指向原型对象的内部指针。
- 让原型对象等于另一个类型的实例,此时原型对象包含执行另一个原型的指针,相应的,另一个原型中也包含指向另一个构造函数的指针,层层递进,构成实例与原型的链条。
- 搜索过程沿着原型链向上:例调用
instance.getSuperValue()
经历的三个搜索步骤:- 搜索实例;
- 搜索
SubType.prototype
; - 搜索
SuperType.prototype
;
- 所有引用类型默认都继承了
Object
- 两种确定原型和实例之间的关系:
instanceof
和isPrototypeOf
。 - 子类型重写超类型的某个方法或添加超类型中不存在的某个方法,一定放在替换原型的语句之后。
-
借用构造函数
- 在子类型的构造函数中调用超类型的构造函数
function SuperType() { this.colors = ["red"]; } function SubType() { // 继承了SuperType SuperType.call(this); }
-
组合继承:使用原型链实现对原型属性和方法的继承,通过构造函数完成对实例属性的继承。(最常用的)
function SuperType(name) {
this.name = name;
this.color = ["red", "blue"];
}
SuperType.prototype.sayName = function() {
console.log(this.name);
}
function SubType(name, age){
// 继承属性
SuperType.call(this, name);
this.age = age;
}
// 继承方法(SuperType中的相同属性会被屏蔽)
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function() {
console.log(this.age);
}