1

JavaScript 简介

JavaScript实现

核心 (ECMAScript)
文档对象模型 (DOM)
浏览器对象模型 (BOM)

ECMAScript

ECMA-262定义是基础
Web浏览器是ECMAScript实现可能的宿主环境之一

宿主环境不仅提供基本的ECMAScript实现,同时也会提供该语言的扩展,以便语言和环境之间对接交互。而这些扩展-如DOM,则利用ECMAScript的核心类型和语法提供更多具体的功能,一边实现针对环境的操作。其它宿主环境包括Node 和 Adobe Flash。

ECMA262标准规定
语法类型语句关键字保留字操作符对象

语法和数据类型

文档对象模型 (DOM)

文档对象模型(DOM, Document Object Model) 是针对XML 进过扩展用于 HTML 的应用程序编程接口 (API, Application Programming Interface).

DOM 把整个页面映射为多个层节点。HTML 或 XML 页面中的每个组成部分都是某种类型的节点,这些节点又包含着不同类型的数据。

浏览器对象模型 (BOM)

提供与浏览器交互的方法和接口

在HTML中使用JavaScript

<scirpt>元素

  • async:可选。表示应该立即下载脚本,但不妨碍页面的其它操作。 比如:下载其他资源或等待加载其它的脚本。只对外部脚本文件有效。
  • charset :可选。表示通过 src 属性指定的代码的字符集。由于大多数浏览器会忽略它的值,因此这个属性很少使用。
  • defer :可选。表示脚本可以延迟到文档完全被解析和显示之后再执行。只对外部脚本文件有

效。IE7 及更早版本对嵌入脚本也支持这个属性。

  • language :已废弃。原来用于表示编写代码使用的脚本语言(如 JavaScript 、 JavaScript1.2或 VBScript )。大多数浏览器会忽略这个属性,因此也没有必要再用了。
  • src :可选。表示包含要执行代码的外部文件。
  • type :可选。可以看成是 language 的替代属性;表示编写代码使用的脚本语言的内容类型(也称为 MIME 类型)。虽然 text/javascript 和 text/ecmascript 都已经不被推荐使用,但一直以来使用的都还是 text/javascript 。实际上,服务器在传送 JavaScript 文件时使用的MIME 类型通常是 application/x–javascript ,但在 type 中设置这个值却可能导致脚本被忽略。另外,在非IE浏览器中还可以使用以下值: application/javascript 和 application/ecmascript 。考虑到约定俗成和最大限度的浏览器兼容性,目前 type 属性的值依旧还是text/javascript 。不过,这个属性并不是必需的,如果没有指定这个属性,则其默认值仍为text/javascript 。

包含在<script>元素内部的 JavaScript 代码将被从上之下一次解释。 在解析引擎对<scirpt>元素内部的所有代码执行王弼后,页面中的其余内容都不会被浏览器加载或显示。

只要不存在 defer 和 async 属性,浏览器都会按照 <script> 元素在页面中出现的先后顺序对它们依次进行解析。换句话说,在第一个 <script> 元素包含的代码解析完成后,第
二个 <script> 包含的代码才会被解析,然后才是第三个、第四个……

标签的位置

以往,所有的<script> 元素都应该放在页面的 <head> 元素中。
作用:所有外部文件(包括 CSS 文件和 JavaScript 文件)的引用都放在相同的地方,把全部的JavaScript代码都被下载,解析,执行完以后,才能开始呈现页面的内容(浏览器在遇到<body>标签时,才开始呈现内容)。对于大型程序,需要很多JavaScript代码的页面来说,会导致浏览器在呈现页面时出现明显的延迟。 现在都习惯把 JavaScript放在<body>元素中页面内容的后面,有些特殊放入在特殊位置。

延迟脚本

<scirpt>标签定义了 defer 属性,这个属性的用途是表明脚本在执行时不会影响页面的构造。也就是说,脚本会被延迟到整个页面都解析完毕后再运行。因此,在 <scirpt> 元素中设置,defer属性,相当于告知浏览器立即下载,但延迟执行。

延迟脚本并不一定会按照顺序执行,也不一定会在 DOMContentLoaded事件触发前执行,因此最好只包含一个延迟脚本。

defer 属性只适用于外部脚本文件

把延迟脚本放在页面底部仍然是最佳选择

异步脚本

指定 async 属性的目的:不让页面等待两个脚本下载和执行,从而异步加载页面其它内容。建议异步脚本不要再加载期间修改DOM.
异步脚本一定会在页面的 load 事件前执行,但可能会在 DOMContentLoaded 事件触发之前或之后执行。

基本概念

语法

区分大小写
ECMAScript 中的一切(变量、函数名和操作符)都区分大小写

标识符
第一个字符必须是一个字母、下划线( _ )或一个美元符号( $ )
其他字符可以是字母、下划线、美元符号或数字

ECMAScript 标识符采用驼峰大小写格式,也就是第一个字母小写,剩下的每个单词的首字母大写

注释
// 单行注释
多行注释

严格模式
严格模式:JavaScirpt定义了一种不同的解析与执行模型。

在严格模式下ECMAScript3 中的一些不确定的行为将得到处理,而且对某些不安全的操作也会抛出错误。

"use strict"

它是一个编译指示(pragma),用于告诉支持的 JavaScript 引擎切换到严格模式。这是为不破坏 ECMAScript 3 语法而特意选定的语法.

// 可以在函数内部包含 这条编译指示
function doSomething(){
    "use strict";
    //函数体
}

语句

ECMAScript 中的语句以一个分号结尾;如果省略分号,则由解析器确定语句的结尾
加上分号也会在某些情况下增进代码的性能,因为这样解析器就不必再花时间推测应该在哪里插入分号了。

变量

ECMAScript 的变量是松散类型,松散类型就是可以用来保存任何类型的数据。每一个变量仅仅是一个用于保存值的占位符。
定义变量时使用var 操作符(var 是一个关键字)后面跟一个变量名 (一个标识符)

用 var 操作符定义的变量将成为定义该变量的作用域中的局部变量。如果在函数中使用var 定义一个变量,那么这个变量在函数推出后就会被销毁。

数据类型

ECMAScript 中有 5 中简单数据类型(基本数据类型): Undefined , Null, Boolean, Number, String. 一种复杂数据类型:Object。
Object本质上是由一组无序的名值对组成的。
ECMAScript不支持任何创建自定义类型的机制。
ECMAScript数据类型具有动态性,因此没有定义其它数据类型的必要。

typeof 操作符
作用:检测数据类型
typeof 是一个操作符而不是函数

"undefined" ——如果这个值未定义;
"boolean" ——如果这个值是布尔值;
"string" ——如果这个值是字符串;
"number" ——如果这个值是数值;
"object" ——如果这个值是对象或 null ;
"function" ——如果这个值是函数。

调用 typeof null 会返回 "object" ,因为特殊值 null 被认为是一个空的对象引用

Undefined 类型

Undefined 类型只有一个值, 特殊的 undefined。 在使用var 声明变量但未对其加以初始化,这个值就是 undefeind。

var a; // 变量声明之后默认就是 undefeind 不需要 指定赋值 undefined
var b = undefined;
a === b; // true

对未初始化的变量执行 typeof 操作符会返回 undefined 值,而对未声明的变量执行 typeof 操作符同样也会返回 undefined 值.

对未初始化和未声明的变量执行typeof 操作符都 返回 了 undefined 值, 因为这两种变量从技术角度有本质区别,但实际上无论对那种变量也不可能执行真正的操作。

Null 类型

Null 类型 值是:null。 null表示一个空对象的指针, typeof操作符检测 null 值时会返回 'object'

如果定义的变量准备在将来用于保存对象,那么最好将该变量初始化为 null。 只需要检测null值, 就可以知道相应的变量是否已经保存了一个对象的引用。

undefined 值是派生自 null 值, 因此 ECMA-262规定对它们的想等测试返回 true .


null == undefiend // true
null === undefiend // false

定义的变量准备当作真正保存对象,就明确的让该变量保存 null 值。 体现 null 作为空对象指针的惯例, 有助于区分 null 和 undefined

Boolean 类型

Boolean 类型 是 ECMAScript 中使用得最多的一种类型,该类型字面值: true 和 false。
这两个值与数字值不是一回事, 因此 true 不一定等于 1, 而 false 也不一定等于 0.

Boolean 类型的字面值 true 和 false 是区分大小写的。 True 和 False (以及其它的混合大小写形式) 都不是 Boolean 值, 只是标识符。

ECMAScript 中所有类型的值都有与这两个Boolean值等价的值。 要将一个值转换对应的Boolean值,可以调用转型函数 Boolean();

false值: false, '' (空字符串) , 0, NaN, null, undefined

Number 类型

Number 类型使用 IEEE754 格式来表示 整数和浮点数值(浮点数值在某些语言中也被称之为双精度数值)。 ECMA-262定义了不同的数值字符量格式:十进制,八进制,十六进制.

浮点数值
数值中必须包含一个小数点,并且小数点后面必须至少有一位数字.

由于保存浮点数值需要的内存空间是保存整数值的两倍, 因此 ECMAScript会不失时机的将浮点数值转换为整数值。

如果小数点后面没有跟任何数字,这个数值就可以作为整数值来保存。如果浮点数值本身表示的就是一个整数(如1.0) , 这个数值也会被转换为整数

var floatNum = 1.; // 小数点后面没有数字 解析成 整数1
var falotNum2 = 10.0; // 整数  解析为 10

对于极大或极小的数值,可以用 e 表示法 (科学记数法) 表示浮点数值。 用 e 表示法表示的数值等于 e 前面的数值 乘以 10 的指数次幂

var floatNum = 3.125e7; // 等于 31250000

浮点数值的最高精度是 17 位小数, 但在进行算术计算时,其精确度远远不如整数。
例如:0.1+0.2 结果并不是 0.3 。 而是 0.30000000000000004

数值范围

ECMAScript 能够表示的最小数值保存在 Number.MIN_VALUE, 大多数浏览器为: 5e-324
能够表示的最大数值保存在 Number.MAX_VALUE ,值为: 1.7976931348623157e+308

超出数值的话,这个数值将被自动转换成特殊的 Infinity 值。

这个数值是负数,则会被转换成 -Infinity (负无穷大) , 如果这个数值是正数,则会被转换成 Infinity (正无穷大)

某次计算返回正或负的 Infinity 值, 那么该值无法继续参与下一次的计算。 因为 Infinity 是不能够参与计算的数值。
确定一个数值是不是有穷(换句话说,是不是位于最大和最小的数值之间),可以使用 isFinite() 函数, 参数是位于最大与最小数值之间返回true。

var result = Number.MAX_VALUE + Number.MAX_VALUE; // Infinity
console.log( isFinite(result) ); //false;
NaN

NaN,非数值(Not a Number) 是一个特殊的数值,这个数值用于表示一个本来要返回数值的操作符未返回数值的情况(这样就不会抛出错误).

任何数值除以 0会返回 NaN
只有 0除以 0 才会返回 NaN,正数除以 0 返回 Infinity,负数除以 0返回-Infinity。

10 / 0 // NaN

NaN特点:

  1. 任何涉及 NaN 的操作 (例如 NaN /10) 都会返回 NaN (多不计算中有可能导致问题)
  2. NaN 与任何值都不相等, 包括NaN 本身 (NaN == NaN //false)

isNaN() 函数, 判定参数是否"不是数值"

isNaN() 在接收到一个值之后,会尝试将这个值转换为数值。某些不是数值的值会直接转换为数值,例如字符串 "10" 或 Boolean
而并不是任何不能被转换为数值的值都会导致这个函数返回true.

isNaN() 在接收到一个值之后,会尝试将这个值转换为数值。
某些不是数值的值会直接转换为数值,例如字符串 "10" 或 Boolean.
不能转换成功的数值的值,isNaN() 返回true .

console.log(isNaN(NaN)); // true
console.log(isNaN(10)); // false(10 是一个数值)
console.log(isNaN("10")); // false(可以被转换成数值 10)
console.log(isNaN("blue")); // true(不能转换成数值)
console.log(isNaN(true)); // false(可以被转换成数值 1)

console.log(isNaN({})); // true 
// isNaN() 适用于对象。在基于对象调用 isNaN(); 函数时,会首先调用对象的valueOf();方法,然后确定该方法返回的值是否可以转为数值,如果不能,则基于这个数值再调用toString();方法,再测试返回值。如果不能返回true.
数值转换
非数值转换为数值: Number(); parseInt(); parseFloat();

Number() : 用于任何数据类型

规则:
Boolean: true -> 1, false -> 0;
Bumber: 数字值, 简单的传入 和 返回;
Null: null -> 0;
Undeinfed: undefined -> NaN;

String:
1: 如果字符串中只包含数字(包含前面带正好或负号的情况),则将其转换为十进制数值。

Number('1');  // 1;
Number('123'); // 123;
Number('011'); // 11; (注意:前导零被忽略);
Number('000000211'); // 211

2: 如果字符串中包含有效浮点格式。 转换为对应的浮点数值 (会忽略前导零);
3: 如果字符串中包含有效的十六进制格式,转换为相同大小的十进制数值;
4: 如果是空字符串, 转换为 0;
5: 字符串中包含其它以外的格式字符串,转换为NaN。

Object: 调用对象的valueOf(); [与对象object相关的原始值(如果存在)。如果没有值与object相关,则返回对象自身。] 然后按照前面的规则转换返回的值。如果转换结果是NaN。
则调用对象的toString(); 方法,然后依照前面的规则转换返回的值。

parseInt(), parseFlaot(); : 把字符串转换成数值 (尽力转换,能给转换的都转换)

是否符合数值模式,会忽略字符串前面的空格,直到找到第一个非空格字符.

如果字符不是数值字符或者负号,就返回 NaN。
parsetInt(); 转换空字符串会返回 NaN (Number() 对空字符串返回0 );

parsetInt(''); // NaN

如果第一个字符是数字字符, parseInt(); 会继续解析第二个字符,直到解析完所有后续字符串遇到一个非字符串字符.

parseInt('123bule'); // 1234  "1234blue" 被转换1234,"blue" 被忽略
parseInt('22.2'); // 22 小数点并不是有效的数字字符
parseInt("0xf"); // 15(十六进制数)

parseFloat() 只解析十进制值,没有用第二个参数指定基数的用法

var num1 = parseFloat("1234blue"); // 1234 (整数)
var num2 = parseFloat("0xA"); // 0
var num3 = parseFloat("22.5"); // 22.5
var num4 = parseFloat("22.34.5"); // 22.34
var num5 = parseFloat("0908.5"); // 908.5
var num6 = parseFloat("3.125e7"); // 31250000

String 类型
由零或多个 16 位 Unicode 字符组成的字符序列

字符字面量
\n 换行 \r 回车

字符串的特点
ECMAScript 中的字符串是不可变的,字符串一旦创建,它的值就是不能改变的。要改变某个变量保存的字符串,首先要销毁原来的字符串,然后再用另一个包含新值的字符串赋值。

转换为字符串
toString(); // 返回相应值的字符串
数值,布尔值,对象和字符串值 (每个字符串都有一个toString();方法,该方法返回字符串的一个副本) 但是 null 和 undefined 没有这个方法

在不知道要转换的值是不是 null 或 undefined 的情况下,还可以使用转型函数 String() ,这个
函数能够将任何类型的值转换为字符串。

转型函数 String(); 能够将任何类型的值转为字符串。
String();转换规则:
如果值有toString(); 方法,则调用该方法(没有参数) 并返回相应的结果。
null -> 'null';
undefined -> 'undefined';

null 和 undefined 没有 toString() 方法,所以 String()函数就返回了这两个值的字面量

Object 类型

对象是一组数据和功能的集合。对象可以通过执行 new 操作符后跟要创建的对象类型的名称来创建。而创建Object 类型的实例并未其添加属性和方法。

Object 类型所具有的任何属性和方法也同样存在于更具体的对象中.

Object 的每个实例都具有下列属性和方法:

constructor:保存着用于创建当前对象的构造函数。
hasOwnProperty(prototyName) : 检测给定的属性在当前对象实例中(而不是在实例的原型中) 是否存在。 参数为属性名,必须以字符串形式指定 o.hasOwnPrototye('name'); //false or true
isPrototypeOf(object); 检查传入的对象是否是传入对象的原型
propertyIsEnumerable(propertyName) :用于检查给定的属性是否能够使用 for-in 语句来枚举。与 hasOwnProperty() 方法一样,作为参数的属性名必须以字符串形式指定。
toLocaleString() :返回对象的字符串表示,该字符串与执行环境的地区对应。
toString() :返回对象的字符串表示。
valueOf() :返回对象的字符串、数值或布尔值表示。通常与 toString() 方法的返回值
相同。

函数

严格模式对函数的一些限制:

  • 不能把函数名命名为eval 或 arguments
  • 不能把参数命名为 eval 或 arguments
  • 不能出现两个命名参数同名的情况

理解参数
在函数体内可以通过 arguments 对象来 访问这个参数数组,从而获取传递给函数的每一个参数。
arguments 对象 只是与数组类似(它并不是 Array 的实例)

ECMAScript函数的一个特点: 命名的参数只提供便利,但不是必需的。

没有传递值的命名参数将自动被赋予undefined值。

严格模式下
从写arguments的值会导致语法错误。

无须指定函数的返回值,任何ECMAScript函数都可以在任何时候返回任何值。
在未指定返回值的函数返回的是一个特殊的 undefiend 值。

ECMAScript中没有函数签名的概念,函数参数是以一个包含零或多个值的数组的形式传递的。
可以向ECMAScript 函数传递任意数量的参数,并且可以通过 arguments 对象来访问这些参数。
由于不存在函数签名的特性,ECMAScript 函数不能重载。

变量&作用域&内存问题

JavaScript 的变量松散类型的本质,决定了它只是在特定时间用于保存特定值的一个名字.
由于不存在定义某个变量必须要保存何种数据类型的值的规则,变量的值及其数据类型可以在脚本的生命周期内改变。
一种既有趣又强大同时又容易出问题的特性。

基本类型和引用类型的值

数据类型:
基本类型: 简单的数据段
引用数据类型:由多个值构成的对象

动态的属性
创建一个变量并未该变量赋值。当这个值保存到变量中以后,对不同类型的值可以执行的操作则不同。
当复制保存着对象的某个变量时,操作的是对象的引用。但是在为对象添加属性时,操作的是实际的对象。

复制变量值
从一个变量想另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该变量复制到为新变量分配的位置上

从一个变量向另一个变量复制引用类型值的时候,会将存储的变量对象中的值复制一份放入到新变量分配的空间中。不同的是,这个值的副本实际上是一个指针,而这个指针指向存储在队中的一个对象。复制操作结束后,两个变量实际上将引用同一个对象。改变其中一个变量,就会影响另一个变量。

var age = 10;
var price = 100;
// 如上:js解释引擎先创建2个变量名:age,price 
// 再为此2个变量申请,各自的变量空间地址,并获得地址,然后age,price的地址属性的值就是内存中的真实地址 
// 也就是说,变量名并不能体现变量的值,只能体现该变量在内存中的位置。 

var a = 3; 

// 1:建立变量,申请空间    2:变量指向该空间,并把值存储到相应内存空间; 

// 传递赋值: 
var b = a; 

// 1: 建立变量,申请空间 2: 变量指向该空间,并获取a变量对应空间的值,存储到内存b的相应空间 

// 变量之间的赋值,var b = a; 为例,不仅可以把a的值读取出来,存储到b的相应空间,这样a,b 是相互独立的,修改互不影响对方; 

传递参数
在向参数传递应用类型的值的时候,会把这个值的内存中的地址复制给一个局部变量,因此这个局部变量的变化会反应在函数内部。

即使在函数内部修改了参数的值,但原始的引用不变。当函数内部重写参数,这个变量引用就是一个局部对象了。而这个局部对象会在函数执行完毕后立即被销毁。(可以把ECMAScript函数的参数想象成是局部变量,写时复制的机制)

function setName ( obj ) {
    obj.name = 'Ni';
    obj = new Object();
    obj.name = 'You';
}

var Person= new Object();
setName(Person);
console.log( Person.name ); // Ni

检测类型
typeof操作符确定一个变量是: 字符串,数值,布尔值,还是undeined 类型

instanceof 操作符检测是否是当前类的实例

执行环境及作用域

执行环境:定义了变量或函数有权访问其它数据,决定了它们各自的行为。
每一个执行环境中都有一个变量对象( variable Object )

某个执行环境中的所有代码执行完毕后,该执行环境会被销毁,保存在其中的所有变量和函数定义也随之销毁(全局执行环境知道应用程序退出--例如关闭网页或者浏览器时才会被销毁)

每个函数都有自己的执行环境。当执行流入一个函数时,函数的环境就会 被推入一个环境栈中。而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。

当代码在一个执行环境中执行时,会创建变量对象的一个作用域链。
作用域链:保证对执行环境有权访问的所有变量和函数的有序访问。

如果这个执行环境是函数,则将其活动对象(activation Object) 作为变量对象。活动对象在最开始时至包含一个变量,即arguments对象(这个对象在全局环境中是不存在的)。

作用域链中的下一个变量对象来之包含(外部)环境,而再下一个变量对象则来自下一个包含执行换。一直延续到全局执行环境,全局执行环境的变量对象始终都是作用域链中的最后一个对象。

标识符解析式沿着作用域链一级一级的搜索标识符的过程。搜索过程始终从作用域链的前端开始,然后逐级的向后回溯,直至找到标识符为止(如果找不到标识符,会导致错误发生)

函数参数也被当作变量来对待,因此其访问规则与执行环境中的其它变量相同。

预编译阶段:

* 分析参数
* 分析变量声明
* 分析函数声明

AO上的属性来至于三个方面:

* 参数,
* 局部变量声明
* 函数声明

首先分析参数,把分析的参数形参AO的属性,如果传染来的实参,则把实参赋值给相应的属性。
其次分析var声明 ,以var str = 'hello'为例: 把str声明为AO的属性,值为undefined.(如果var 声明的变量名与形参名称一致. 不产生影响., 因为AO的str属性已经存在不产生影响.)
最后,分析函数声明,(匿名匿名函数不可以) 函数在js中就是变量(高阶函数)

function 函数名() {};

函数声明,假设函数名为fn, 函数声明会把函数赋值为AO的fn属性的值。

延长作用域链

执行环境的类型总共只有两种:全局和局部(函数),有其它办法来延迟作用域链。有些特殊的语句可以在作用域链的前端临时增加一个变量对象,该变量对象会在执行后被移除。

执行流入下列任何一个语句时,作用域链就会得到加长。

  • try-catch语句中的catch块。
    catch语句,会创建一个新的变量对象,其中包含的是被抛出的错误对象的声明。
  • with语句
    会将制定对象添加到作用域链中。

没有块级作用域

声明变量

使用 var 声明的变量会自动被添加到最接近的执行环境中。在函数内部,最接近的执行环境就是函数的局部环境。
在with语句中,最接近的执行环境是函数环境。
如果初始化变量时没有使用var 声明,该变量会自动被添加到全局执行环境。

查询标识符

在某个执行环境中为了读取或写入而引用的一个标识符时,必须通过搜索来确定该标识符实际代表什么。搜索过程从作用域链的前端开始,向上逐级查询与给定名字匹配的标识符。

如果在局部执行环境中找到该标识符,搜索过程停止,变量就绪。如果在局部执行环境中没有找到该变量名,则继续沿作用域链向上搜索,搜索过程将一直追溯到全局环境的变量对象。如果在全局环境中也没有找到这个标识符,则该变量没有声明。

如果局部执行环境与父级执行环境中存在相同的标识符,则使用局部执行环境的标识符。

垃圾收集

执行环境会负责管理代码执行过程中使用的内存。

原理:找出那些不再继续使用的变量,然后释放器占用的内存。
执行:垃圾收集器会按照固定的时间间隔(或代码执行中预定的收集时间),周期性的执行这一操作。

标记清除

垃圾收集器:给存储在内存中的所有变量都加上标记(可以使用任何标记方式),然后去除执行换种的变量以及被执行环境中 变量引用的变量的标记。最后完成内存清除,销毁带标记的值,并且回收所占用的内存空间。

//垃圾收集  ,标记清除 (模拟)
function test () {
    
    var a = 10;   //tick - 被使用
    var b = 20;   //tick - 被使用
    
}
test();  //执行完毕 之后 ,a,b又被标记使用。  tick- 没有被使用
//在间隔时间中 回收。  如果tick 没有被使用, 则回收。
// 先在执行环境中全局被标记,然后去除为被使用的标记,取出这些标记。

引用计数
引用计数:跟踪记录每一个被引用的次数。当引用次数为0的时候垃圾回收机制清理
缺点:会产生循环引用

//引用计数(模拟)
//如果变量被引用 ,  count = 1;
function test2 () {

    var a = 10;   //count = 1;
    var b = 20;

    var c;
    c = a;  //count++ = 2; //a 被 c 所使用 ,引用。

    a = 50;  //count--; //重新被赋值 count--   //等待 count 为 0 的时候, 垃圾回收机制  就回收

}

性能问题

IE的垃圾收集器更具内存分配量运行
条件:达到:256 个变量、4096 个对象(或数组)字面量和数组元素(slot)或者 64KB 的字符串 的任和一个临界值。垃圾收集器就会运行。

管理内存

解除引用:数据不再使用,最好设置为null手动释放引用 (范围:大多数全局变量和全局对象的属性)

效果:占用最少的内存让页面性能更好。优化内存的最佳方式,为执行中的代码值保存必要的数据。

解除引用并不是自动回收该值所占用内存。是让值脱离执行环境,被标记上没有使用,让垃圾收集器下次运行时将其回收释放该值所占用的内存。

V8分配JavaScript对象结构

V8的性能提升关键三个部分:

  1. 快速属性访问
  2. 动态机器码生成
  3. 高校的垃圾收集
[ class / map ] -> ... ; 指向内部类
[ properties  ] -> [empty array]
[ elements    ] -> [empty array] ; 数值类型名称的属性
[ reserved #1 ] -\
[ reserved #2 ]  |
[ reserved #3 ]  }- in object properties,即预分配的内存空间
...............  |
[ reserved #N ] -/

在创建新的对象时,V8会创建某个预分配的内存区域来存放所谓的in-object属性,预分配区域的大小由构造函数中的参数目录决定(this.field=expr).当你打算向对象中添加某个属性时,V8首先会尝试放入所谓的in-order槽位中。当in-object槽位过载之后,V8会尝试将心的属性添加到out-of-obejct属性列表。而属性名与属性下标映射关系即存放所谓隐藏累中。
例如:{a: 1, b: 2, c: 3, d: 4}对象的存储方式可能如下:

[ class         ] -> [a: in obj #1, b: in obj #2, c: out obj #1, d: out obj #2]
[ properties     ] -> [ 3 ] [ 4 ]; this is linear array
[ elements     ]
[ 1 ]
[ 2 ]

随着属性数目的增加,V8会转回到传统的字典模式/哈希表模式:

[ class ] -> [ OBJECT IS IN DICTIONARY MODE ]
[ preperties ] -> [a: 1, b: 2, c: 3, d: 4, e: 5]; this is classical hash tab
[ elements ]

引用类型

引用类型的值(对象)是引用类型的一个实例

Object类型

表达式上下文: 能够返回一个值(表达式)

对象字面量是向函数传递大量可选参数的首选方式。

Array 类型

数组最多可以包含: 4294967295。 如果想添加就会发生异常。可能会导致运行时间超长的脚本错误。

检测数组

instanceof操作符: 如果网页中存在多个框架,那么实际上就存在两个以上不同的全局执行环境,从而存在两个以上的不同版本的Array构造函数。

如果从一个框架向另一个框架传入一个数组,那么传入的数组与在第二个框架中原生创建的数组分别具有不同的构造函数。

if ( value instaceof Array ) {}

Array.isArray() : 确定某个值到底是不是数组,而不管它是在那个全局执行环境中创建的。

转换方法
所有对象都具有 toLocaleString(); toString(); valueOf(); 方法

arr.toString(): 会返回由数组中的每个值的字符串形式拼接而形成的一个以逗号分割的字符串。
arr.valueOf(): 返回的还是数组

var arr = [1, 2, 3, 5];
alert( arr.toString() ); // 1,2,3,5
alert( arr.valueOf() ); // 1,2,3,5
alert( arr ); // 1,2,3,5
// alert(); 要接受字符串参数,所以JavaScript引擎会在后台调用toString(); 方法。

toLocaleString(); 会返回与toString(); 和 valueOf(); 方法相同的值, 但是也不是总是如此。当调用数组的toLocaleString(); 方法时,会创建一个数组值以逗号分隔的字符串。
不同之处:为了取每一项的值调用的是每一项的toLocaleString(); 而不是toString(); 方法。

var person1 = {
    toLocaleString : function () {
        return "Nikolaos";
    },
    toString : function() {
        return "Nicholas";
    }
};

var person2 = {
    toLocaleString : function () {
        return "Grigorios";
    },
    toString : function() {
        return "Greg";
    }
};

var people = [person1, person2];

alert(people); //Nicholas,Greg
alert(people.toString()); //Nicholas,Greg
alert(people.toLocaleString()); //Nikolaos,Grigorios

如果数组中的某一项值是 null 或者undefined 那么 在 join(); toLocaleString(); toString(); valueOf(); 方法返回的结果中以空字符串表示

栈方法
栈LIFO(Last-In-First-Out),(后进先出)的数据结构 最新添加被最早移除。
推入:栈中项插入
弹出:栈中项移除

push(); pop();

队列方法
队列数据结构: FIFO(First-In-First-Out)(先进先出)。
列表末端添加项
列表前端移除项

unshift(); shift();

重排序方法
reverse(); sort();
返回值是经过排序之后数组。影响原数组

// sort(); 接受一个参数作为参数
function compare ( val1, val2 ) {
    
    val1 < valu2 ? 1 : -1;

}
sort(conpare);

操作方法
concat() : 连接数组。不影响原来数组
先创建当前数组的副本,然后将接受到的参数添加到这副本的末尾,返回新构建的数组。
在没有给concat()方法传递参数的情况下,只是复制当前数组并返回副本。如果传递的值不是数组,值会被简单的添加到结果数组的末尾。

slice(); 拆分数组,不影响原来数组
基于当前数组中的一或多个项创建一个新数组。
参数:返回项的起始和结束位置。
一个参数:返回从该参数指定位置开始到当前数组的末尾所有项
二个参数:返回起始位置和结束位置之间的项(不包括结束位置的项)

如果 slice() 方法中的参数有负数,则用数组的长度加上概述来确定相应的位置

var arr = [1, 2, 3, 4, 5];
slice(-2, -1); // slice(3, 4);

如果结束位置小于起始位置,返回空数组。

splice(): 向数组中部插入项(主要用法)

arr.splice(开始项,[控制几位数,值]);

splice()方法始终都会返回一个数组,该数组中包含从原始数组中删除项(如果没有删除任何一项,返回一个空数组)

位置方法

indexOf() 和 lastIndexof();
返回要查找的项(查找的项必须严格相等。就像是用 === 运算符判断)所在的数组中的位置,没有查找到的情况下返回 -1

迭代方法

  • every() :对数组中的每一项运行给定函数,如果该函数对每一项都返回 true ,则返回 true
  • filter() :对数组中的每一项运行给定函数,返回该函数会返回 true 的项组成的数组。
  • forEach() :对数组中的每一项运行给定函数。这个方法没有返回值。
  • map() :对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。
  • some() :对数组中的每一项运行给定函数,如果该函数对任一项返回 true ,则返回 true

Date类型

Date 类型保存的日期能够精确到 1970.1.1 之前 或之后的 285616年

Date.parse(); 返回接受日期的毫秒数。
参数:日期的字符串参数, 如果不是字符串表示,则返回NaN。

Date.now(); 返回调用这个方法时的日期和时间的毫秒数

+new Date(); 使用+操作符把 Data 对象转换成字符串. 把日期格式转成 毫秒数

继承的方法

Date 类型重写了 toLocaleString(); toString(); valueOf();

toLocaleString() 方法会按照浏览器设置的地区相适应的格式返回日期和时间。
时间格式会包含AM或PM,但不会包含时区信息(浏览器而异)

"2016/7/20 下午2:13:04"

toString() 方法返回带有时区信息的日期时间

valueOf() 返回毫秒数

日期格式化方法

  • toDateString() ——以特定于实现的格式显示星期几、月、日和年;
  • toTimeString() ——以特定于实现的格式显示时、分、秒和时区;
  • toLocaleDateString() ——以特定于地区的格式显示星期几、月、日和年;// "2016/7/20"
  • toLocaleTimeString() ——以特定于实现的格式显示时、分、秒; // "下午2:16:46"
  • toUTCString() ——以特定于实现的格式完整的 UTC 日期。

日期/时间组件方法

  • getTime() 日期的毫秒数, 与valueOf() 返回值相同
  • getFullYear(); 年
  • geMonth(); 月
  • getDate(); 日, 日期月份中的天数(1到31)
  • getDay(); 星期, 日期中星期的星期几(其中0表示星期日,6表示星期六)
  • getHours(); 小时
  • getMinutes(); 分
  • getSeconds(); 秒
  • getMilliseconds(); 毫秒

RegExp 类型

模式:

  • g :全局(global)模式,应用于所有字符串,而非在发现第一个匹配项时立即停止
  • i : 不区分大小写(case-insensitive)模式,确定匹配项时忽略模式与字符串的大小写。
  • m : 多行(multiline)模式,在到达一行文本末尾时还会继续查询下一行中是否存在与模式匹配的项。

RexExp构造函数 创建正则:
参数1: 匹配的字符串模式
参数2:可选的标志字符串

new RegExp('[bc]at', 'i');

RegExp 实例属性

global: 布尔值,是否设置了 g 标志
ignoreCase : 布尔值 是否设置了 i 标志
laseIndex : 整数, 开始搜索下一个匹配项的字符位置, 从0算起。
multiline: 布尔值 是否设置了 m 标志
source : 正则表达式的字符串表示,按照字面量形式非传入构造函数中的字符串模式返回

var pattern1 = /\[bc\]at/i;
alert(pattern1.global); //false
alert(pattern1.ignoreCase); //true
alert(pattern1.multiline); //false
alert(pattern1.lastIndex); //0
alert(pattern1.source); //"\[bc\]at"

var pattern2 = new RegExp("\\[bc\\]at", "i");
alert(pattern2.global); //false
alert(pattern2.ignoreCase); //true
alert(pattern2.multiline); //false
alert(pattern2.lastIndex); //0
alert(pattern2.source); //"\[bc\]at"

RegExp 实例方法

exec(); 返回 数组 或 null。

exec是英语execute的意思,CEO首席执行官,E就是executive执行的
“执行” 把正则式放到字符串上执行

每次执行结果按序输出,不管结果有几个,一次只输出一个 ,如果多次输出,会保持前面的引用。当匹配超过原字符串的时候,会返回null。然后遇到null,指针返回到匹配的字符的第一位。 具有迭代器的感觉。

var str = 'ABCDEFG1234567abcdefg';
var reg = /[a-z]/g;
console.log( a=/[a-z]/g.exec(str) );

var a;
while( a=reg.exec(str) ){  //这边 null 为 fasle。 exec() 会保持对前面一次的引用。 需要使用 值来赋值。
    console.log( a );
}

test(); 目标字符串与某个模式是否匹配,但不需要知道其文本内容。
返回布尔值

RegExp 实例继承 toLocaleString(); 和 toString(), valueOf() 都会返回正则表达式自字面量。

RegExp 构造函数属性

RegExp.$1 、 RegExp.$2 … RegExp.$9 ,分别用于存储第一、第二……第九个匹配的捕获组.
在调用exec(); 或 test() 方法时, 这些属性会被自动填充。

Function 类型

每一个函数都是Fcuntion类型的实例,而且都有与其它引用类型一样具有属性和方法。
由于函数是对象,因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定,函数就是变量。

Fcuntion 构造函数定义函数:
Function构造函数可以接收任意的数量的参数,但最后一个参数被看成函数体,而前面的参数则枚举出了新函数的参数。


var sum = new Function('num1', 'num2', 'return num1 + num2');

缺点:
导致解析两次代码,影响性能 . (函数是对象,函数名是指针)
1:解析常规ECMAScript代码
2:解析传入构造函数中的字符串
由于函数名仅仅是指向函数的指针,因此函数名与包含对象指针的其它变量没有什么不同。

没有重载
重写原来的代码,会覆盖前面的同名函数。

函数声明与函数表达式

解析器在向执行环境中加载数据时,对函数声明和函数表达式并非一视同仁。解析器会率先读取函数声明,并使其在执行任何代码之前可用(可以访问)。 函数表达式,则必须等待解析器执行到它所在的代码行,才会真正的被解释执行。

解析器通过一个名为函数声明提升(function declaraion hoisting)的过程,读取并将函数声明添加到执行环境中,对代码求值时,JavaScript引擎在第一遍会声明函数并将他们放到源代码数的顶部。

可以同时使用函数声明和函数表达式。 例如: var sum = function sum () {}; // 在Safari中 会导致错误。

作为值的函数
高阶函数:ECMAScript中函数名本身就是变量,所以函数可以作为值来使用。可以作为参数传递,也可以当作一个函数的返回值将其返回。(从一个函数中返回另一个函数)

函数内部属性

函数内部,特殊的对象: argument 和 this。
arguments:类数组对象,包含着传入函数中的所有参数,arguments的主要用途是保存函数的参数,但这个对象有一个 callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数。

this : this 引用的是这个函数据以执行的环境对象。
函数的作用域,不取决于运行时的环境,而取决于函数声明时的环境.

window.color = "red";
var o = { color: "blue" };

function sayColor(){
    alert(this.color);
}

sayColor(); //"red"
o.sayColor = sayColor;
o.sayColor(); //"blue"

// 即使是在不同的环境中执行,全局的 sayColor() 函数与 o.sayColor() 指向的仍然是同一个函数。 (函数名是一个包含指针的变量)

caller : 调用当前函数的函数引用。 如果在全局作用域中调用当前函数,它的值为null。

严格模式下,访问arguments.callee会导致错误。
ECMAScript5定义arguments.caller 属性,严格模式下访问它导致错误,非严格模式下访问值为undefined。
作用: 分清arguments.caller 和函数的 caller 属性。
严格模式下,caller是只读的,不能为函数的caller属性赋值,否则会导致错误。

函数属性和方法
每个函数包含属性:length ,prototype
length属性表示函数接收到命名参数的个数
prototype:保存它们所有实例的方法。

ECMAScript中引用类型,prototype保存所有实例方法,例如:toString() 和valueOf()等方法实际上都保存prototype属性下
ECMAScript5中,prototype属性是不可枚举的。

每个函数都包含两个非继承而来的方法:apply(); 和 call();
用途:特定的作用域中调用函数。 设置函数体内的this对象值。(扩充函数运行的作用域)

fn.call(obj);
让fn以运行,并且fn中的this以obj身份运行

在严格模式下,未指定环境对象而调用函数,则this值不会转型为window。this值将为undefined

bind : 会创建函数的实例,其this值会被绑定传递给bind()函数的值.

window.color = 'red';

var o = {
    color: 'blue'
}

function sayColor () {
    alert(this.color);
}

var objectSayColor = sayColor.bind(o);

objectSayColor();

每个函数继承的 toLocaleString(); 和 toString(); valueOf() 方法始终都函数函数的代码。

基本包装类型

var s1 = 'some text';
var s2 = s1.substring(2);

步骤:
1:创建String()类型的一个实例
2:在实例上调用指定的方法
3:销毁这个实例

// 类似过程
var s1 = new String('some text');
var s2 = s1.substring(2);
s1 = null;

引用类型与基本包装类型的主要区别:对象的生存期
使用new操作符创建的引用类型实例,在执行流离开当前作用域之前都一直保存在内存中。而自动创建的基本包装类型的uxiiang,则只存在于一行代码的执行瞬间,然后立即被销毁。 意味着不能在运行时为基本类型添加属性和方法。

var s1 = 'some text';
s1.color = 'red';
alert(s1.color); // undeifned

Object 构造函数会像工厂方法一样,根据传入值的类型返回相应基本包装类型的实例。

var obj = new Object(true);
console.log( obj instanceof Boolean ); // true

使用 new 调用基本包装类型的构造函数,与直接调用同名的壮行函数式不一样。

var value = "25";

var number = Number(value); //  转型函数
alert(typeof number); //"number"

var obj = new Number(value); // 构造函数
alert(typeof obj); //"object"

Boolean 类型

重写了valueOf() 方法,返回基本类型true或false,重写了toString()方法,返回字符串 "true" 和 "false"

var t = new Boolean(true);
console.log(t); // Boolean {[[PrimitiveValue]]: true}

Number 类型

重写了valueOf(); toLocaleString(); toString(); 方法
valueOf(); 返回对象表示的基本类型的数值
toString(); 返回字符串形式的数值。

var num = new Number(19);
console.log(num); // Number {[[PrimitiveValue]]: 100}
  
  

String 类型
继承的valueOf() , toLocaleString(); toString(); 方法都返回对象所表示的基本字符串值
String类型每个实例都有一个length属性,表示字符串包含多少个字符.

var str = new String('hello');
console.log(str); // String {0: "h", 1: "e", 2: "l", 3: "l", 4: "o", length: 5, [[PrimitiveValue]]: "hello"}

字符串的模式匹配方法
match();
参数:正则表达式或者RegExp对象,只有一个参数。

serach(); 查找

replace(); 替换

参数1: RegExp对象或者一个字符串(字符串不会被转成正则表达式)
参数2: 替换的字符串或一个函数。如果参数是字符串,那么替换第一个子字符串。如果需要替换所有子字符串,则提供一个正则表达式(需指定全局(g)标志)。
如果参数是一个函数:
在只有一个匹配项的情况下,会向这个函数传递3个参数:模式的匹配项,模式匹配项在字符串中的位置,和原始字符串。
在正则表达式中定义了多个捕获组的情况下,函数的参数: 模式匹配项,第一个捕获组匹配项,第二个捕获组匹配项,最后两个参数是模式匹配项在字符串中的位置和原始字符串。
该函数返回一个字符串,表示应该被替换的字符串。

split(); 分割字符串,返回数组
基于置顶的分隔符将一个字符串分割成多个字符串,并将结果放在一个数组中。分隔符可以是字符串,可以是一个RxgExp对象。 可以接受可选的第二个参数,用于指定数组的大小。

var colorText = "red,blue,green,yellow";
var colors1 = colorText.split(","); // ["red", "blue", "green", "yellow"]
var colors2 = colorText.split(",", 2); // ["red", "blue"]
var colors3 = colorText.split(/[^\,]+/); // ["", ",", ",", ",", ""]

单体内置对象

不必显式的实例化内置对象,因为已经实例化成功了。
例如: Object ,Array, String,
单体内置对象:Global和Math

Global 对象
isNaN() 、 isFinite() 、 parseInt() 以及 parseFloat()
encodeURIComponent() 方法可以对 URI(Uniform Resource Identifiers,通用资源标识符)进行编码,以便发送给浏览器。
decodeURIComponent() 能够解码使用 encodeURIComponent() 编码。

eval(); 方法
参数: 要执行的ECMAScript(或JavaScirpt)字符串

当解析器发现代码中调用eval()方法时,会将传入的参数当作ECMAScript解析,然后把执行结果插入到原位置。
通过eval()执行的代码被认为包含该次调用的执行环境的一部分。被执行的代码具有与该执行环境相同的作用域链。
eval()执行的代码可以引用在包含环境中定义的变量。

在 eval() 中创建任何变量或函数都不会被提升,因为在解析代码的时候,它们被包含在一个字符串中,只在eval()执行的时候创建。

严格模式下:外部访问不到eval()中创建的任何变量或函数。为eval赋值也会导致错误。

能够解释代码的字符串能力强大,也非常危险。如果执行用户输入数据的情况,有可能会恶意输入威胁站点或应用程序安全的代码(所谓的代码注入)

Math对象
为保存数学公式和信息提供了一个公共位置

Math.ceil() 向上舍入,即它总是将数值向上舍入为最接近的整数;
Math.floor() 向下舍入,即它总是将数值向下舍入为最接近的整数;
Math.round() 标准舍入,即它总是将数值四舍五入为最接近的整数

Math.random(); 随机数
值 = Math.floor(Math.random() * 可能值的总数 + 第一个可能的值)


alogy
1.3k 声望119 粉丝

// Designer and Developer


« 上一篇
AngularJs
下一篇 »
Gulp