js的知识点自我巩固

168 阅读12分钟

js知识点笔记,有点抄文章的感觉。自学《JavaScript高级程序设计》,笔记采用这本书的。

1.js的介绍和实现

1.1.js的组成

js应该由三部分组成实现:

  • 核心:ECMAScript
  • 文档对象模型:DOM(Document Object Model)
  • 浏览器对象模型:BOM(Browser Object Model)

1.2.ECMAScript

ECMAScript是通过ECMA-262标准化的脚本程序设计语言,往往就被成为JavaScript,其实可以当成是js的一种规范标准。

1.3.DOM

文档对象模型(DOM,Document Object Model)是针对XML但经过扩展用于HTML 的应用程序编程接口(API,Application Programming Interface)DOM把整个页面映射为一个多层节点结构。借助DOM提供的API,开发人员可以轻松自如地删除、添加、替换或修改任何节点。

1.4.BOM

从根本上讲,BOM只处理浏览器窗口和框架,开发人员使用BOM可以控制浏览器显示的页面以外的部分,DOM的一般功能:

  • 弹出新浏览器窗口的功能; 
  • 移动、缩放和关闭浏览器窗口的功能; 
  • 提供浏览器详细信息的navigator 对象; 
  • 提供浏览器所加载页面的详细信息的location 对象; 
  • 提供用户显示器分辨率详细信息的screen 对象; 
  • 对cookies 的支持; 
  • 像XMLHttpRequest 和IE 的ActiveXObject 这样的自定义对象。

2.js在页面中的使用

2.1.js标签的位置

<script>标签一般来说是放在<body>中,因为在浏览器加载的时候,遇到<body>才开始呈现内容,如果说<script>放在<head>中的话,浏览器必须得等全部的js代码都被下载,解析和执行完成后才能呈现内容,当<script>内容很多的时候,这无疑会使得页面的内容加载起来变慢,所以<script>一般是放在<body>中。

不过凡事都有例外,<script>标签定义的有defer属性,这是延迟属性,使用后会在页面全部执行后再运行js;同时还有asyns属性,这是异步脚本,不让页面等待js的加载和执行,但是建议异步脚本不要再加载期间修改DOM

2.2.文档类型(doctype)

IE5.5映入了文档模式的概念,而文档模式是通过文档类型来切换的。

文档模式有两种:标准模式和混杂模式

  • 在标准模式中,浏览器以其支持的最高标准呈现页面;
  • 在混杂模式中,页面以一种比较宽松的向后兼容(兼容老的版本)的方式显示。混杂模式通常模拟老式浏览器的行为以防止老站点无法工作。

而文档类型的书写方式有三种:

  1. 过度型Transitiona,这种文档对标记语法要求不是很严格。
  2. 严格型strict,这种文档对标记语法要求严格。
  3. 框架型frameset当网页设计中有框架元素,就用这种文档类型.

不过在现在的HTML5中,只需要一句就ok了。

至于为什么,我说不好,网上找一句:HTML 5 不基于 SGML,因此不需要对 DTD 进行引用,但是需要 doctype 来规范浏览器的行为。

3.基本概念

3.1.数据类型(坑多)

ES公布了5种简单数据类型(基本数据类型):Undefined、Null、Boolean、Number 和String;还有一种复杂数据类型:Object。

**但是!**可以用tyoeof操作符来检测变量的操作类型,要注意:

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

一般来说,只申明不经初始化的结果就是undefined:

var a ;

现在这个a就是underfined;

而且对于underfined==null,返回值是true。

number类型要看我的另外的博客了,这里说点好玩的:

NaN,非数值(Not a Number), 这个数值用于表示一个本来要返回数值的操作数未返回数值的情况,在ES规范中,任何书除以0都会返回NaN,而不会影响其他代码的运行。

NaN有两个特点:

  • 任何设计NaN的操作都会返回NaN
  • NaN与任何值都不相等:(NaN==NaN)会返回false

其实还是有争议的:但实际上只有0 除以0 才会返回NaN,正数除以0返回Infinity,负数除以0返回-Infinity。

有3个函数可以把非数值转换为数值:Number()、parseInt()和parseFloat()。

  1. 百度吧,不想写了。number就是把其他类型转换成number类型,有特殊情况,百度。
  2. parseInt能识别出开头的整数部分,百度
  3. parseFloat,能识别出小数部分。

3.2.数据操作

位运算提一下:或(|),与(&),非(~),异或(^),左移(<<),右移(>>)

容易犯的错误:

var num1 = 5;
var num2 = 10;
var message = "The sum of 5 and 10 is " + num1 + num2;

输出是**The sum of 5 and 10 is 510**

for-in语句:

for-in语句是一种精准的迭代语句,可以用来枚举对象的属性。对于数组的话,它和for循环差不多,但是要遍历object的话只能使用for-in了。

var obj = {  
   q: "1",  
   w: "2",  
   e: "3"  
}  
for(var v in obj){  
  document.write(v);  
}

 其实结果是qwe

3.3.函数

严格模式对函数的限制:

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

es不接受重载,若是在es中定义了两个同名的函数,那么名字只属于后命名的函数。

3.3.箭头函数

4.各类方法

4.1.sort()

sort()方法按升序排列数组项——即最小的值位于最前面,最大的值排在最后面。 为了实现排序,sort()方法会调用每个数组项的toString()转型方法,然后比较得到的字符串,以确定如何排序。

但是,它的比较是用字符串的比较,也就是一项一项比较,这样的话,5和10比较是先用5和1比较,因为第一项就大,它会直接判断5大于10.

所以要想用这个方法,就得另嵌套一个比较函数:

var values = [0, 1, 5, 10, 15];
values.sort(compare);
alert(values); //0,1,5,10,15

function compare(value1, value2) {
if (value1 < value2) {
return -1;
} else if (value1 > value2) {
return 1;
} else {
return 0;
}
}

若是想返回从大到小,只要改了上面的大小符号就行了。

4.2.数组的迭代方法

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

传入这些方法中的函数会接收三个参数:数组项的值、该项在数组中的位置和数组对象本身。

例如:

var numbers = [1,2,3,4,5,4,3,2,1];
var everyResult = numbers.every(function(item, index, array){
return (item > 2);
});

这里只用item就行了。这个函数返回false;

var someResult = numbers.some(function(item, index, array){
return (item > 2);
});
alert(someResult); //true

forEach()类似for循环,只是进行操作,么有返回值。其他方法不改变原数组。

4.3.RegExp类型

用来支持正则表达式

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

下面的有点多,我直接照抄书上的了:

/*
* 匹配字符串中所有"at"的实例
*/
var pattern1 = /at/g;
/*
* 匹配第一个"bat"或"cat",不区分大小写
*/
var pattern2 = /[bc]at/i;
/*
* 匹配所有以"at"结尾的3 个字符的组合,不区分大小写
*/
var pattern3 = /.at/gi;

转义:

/*
* 匹配第一个" [bc]at",不区分大小写
*/
var pattern2 = /\[bc\]at/i;

RegExp实例的属性:

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

4.4.函数声明与函数表达式

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

alert(sum(10,10));
function sum(num1, num2){
return num1 + num2;
}

上面这个是可以执行的;

但是,下面这个:

alert(sum(10,10));
var sum = function(num1, num2){
return num1 + num2;
};

无法执行,会报错。

4.5.作为值的函数

因为ECMAScript 中的函数名本身就是变量,所以函数也可以作为值来使用。也就是说,不仅可以 像传递参数一样把一个函数传递给另一个函数,而且可以将一个函数作为另一个函数的结果返回。

function add10(num){
return num + 10;
}
var result1 = callSomeFunction(add10, 10);
alert(result1); //20
function getGreeting(name){
return "Hello, " + name;
}
var result2 = callSomeFunction(getGreeting, "Nicholas");
alert(result2); //"Hello, Nicholas"

4.6.函数的内部属性

函数内部有两个特殊对象:argument 和 this。

argument是一个类数组对象,包含着传入函数中的所有参数。它有一个名叫callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数。

一般类似于递归的函数,就是函数里嵌套同名函数的话,最好使用callee属性:

function factorial(num){
if (num <=1) {
return 1;
} else {
return num * factorial(num-1)
}
}

这种,可以改成下面这种:

function factorial(num){
if (num <=1) {
return 1;
} else {
return num * arguments.callee(num-1)
}
}

这是为了消除紧密耦合的现象。在这个重写后的factorial()函数的函数体内,没有再引用函数名factorial。这样,无论引用 函数时使用的是什么名字,都可以保证正常完成递归调用。

this可以这样理解,一般来说是谁调用就指向谁,但是apply,bind,call函数除外。

window.color = "red";
var o = { color: "blue" };
function sayColor(){
alert(this.color);
}
sayColor(); //"red"
o.sayColor = sayColor;
o.sayColor(); //"blue"

this引用的是函数据以执行的环境对象。

ECMAScript 5 也规范化了另一个函数对象的属性:caller。

4.7.函数属性和方法

ECMAScript 中的函数是对象,因此函数也有属性和方法。每个函数都包含两个属性:length 和prototype。length 属性表示函数希望接收的命名参数的个数,就是圆括号里面元素的个数。prototype 是保存引用类型所有实例方法的真正所在。

**每个函数都包含两个非继承而来的方法:apply()和call()。**这两个方法的用途都是在特定的作 用域中调用函数,实际上等于设置函数体内this 对象的值。

apply():

apply()方法接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组。其中,第二个参数可以是Array 的实例,也可以是 arguments 对象。

function sum(num1, num2){
return num1 + num2;
}
function callSum1(num1, num2){
return sum.apply(this, arguments); // 传入arguments 对象
}
function callSum2(num1, num2){
return sum.apply(this, [num1, num2]); // 传入数组
}
alert(callSum1(10,10)); //20
alert(callSum2(10,10)); //20

call():

call()方法与apply()方法的作用相同,它们的区别仅在于接收参数的方式不同。对于call()方法而言,第一个参数是this值没有变化,变化的是其余参数都直接传递给函数。在使用 call()方法时,传递给函数的参数必须逐个列举出来:

function sum(num1, num2){
return num1 + num2;
}
function callSum(num1, num2){
return sum.call(this, num1, num2);
}
alert(callSum(10,10)); //20

传递参数并非apply()和call()真正的用武之地;它们真正强大的地方是能够扩充函数赖以运行的作用域。

window.color = "red";
var o = { color: "blue" };
function sayColor(){
alert(this.color);
}
sayColor(); //red
sayColor.call(this); //red
sayColor.call(window); //red
sayColor.call(o); //blue

这个例子是在前面说明this 对象的示例基础上修改而成的。这一次,sayColor()也是作为全局 函数定义的,而且当在全局作用域中调用它时,它确实会显示"red"——因为对this.color 的求值会转换成对window.color的求值。而sayColor.call(this)sayColor.call(window),则是两 种显式地在全局作用域中调用函数的方式,结果当然都会显示"red"。但是,当运行sayColor.call(o)时,函数的执行环境就不一样了,因为此时函数体内的this 对象指向了o,于是结果显示的是"blue"。

就是不需要o.sayColor=sayColor这个步骤了。

使用call()(或apply())来扩充作用域的最大好处,就是对象不需要与方法有任何耦合关系。

ECMAScript 5 还定义了一个方法:bind():

这个方法会创建一个函数的实例,其this 值会被绑 定到传给bind()函数的值。

window.color = "red";
var o = { color: "blue" };
function sayColor(){
alert(this.color);
}
var objectSayColor = sayColor.bind(o);
objectSayColor(); //blue

sayColor()调用bind()并传入对象o,创建了objectSayColor()函数。objectSayColor()函数的this 值等于o,因此即使是在全局作用域中调用这个函数,也会看到"blue"。

也不要o.sayColor=sayColor这个步骤了。甚至**objectSayColor();**就足够了,不需要o.objectSayColor();

4.8.Number类型的方法

toString()

var num = 10;
alert(num.toString()); //"10"
alert(num.toString(2)); //"1010"
alert(num.toString(8)); //"12"
alert(num.toString(10)); //"10"
alert(num.toString(16)); //"a"

toFixed()

toFixed()方法会按照指定的小数位返回数值的字符串表示,它有四舍五入的特性:

var num = 10;
alert(num.toFixed(2)); //"10.00"
var sum = 10.005;
alert(sum.toFixed(2)); //"10.01"

toExponential()

toExponential(),该方法返回以指数表示法(也称e表示法)

var num = 10;
alert(num.toExponential(1)); //"1.0e+1"

4.9.Global对象

URI编码方法:

Global对象的encodeURI()和encodeURIComponent()方法可以对URI(Uniform Resource Identifiers,通用资源标识符)进行编码,以便发送给浏览器。有效的URI 中不能包含某些字符,例如空格。而这两个URI编码方法就可以对URI进行编码,它们用特殊的UTF-8编码替换所有无效的字符, 从而让浏览器能够接受和理解。

eval()方法:

  • eval()是整个ECMAScript语言中最强大的一个方法,eval() 方法就像是一个完整的ECMAScript 解析器,它只接受一个参数,即要执行的ECMAScript (或JavaScript)字符串。
  • 当解析器发现代码中调用eval()方法时,它会将传入的参数当作实际的ECMAScript语句来解析,然后把执行结果插入到原位置。
  • eval()中创建的任何变量或函数都不会被提升,因为在解析代码的时候,它们被包含在一个字符串中;它们只在eval()执行的时候创建。
  • 严格模式下,在外部访问不到eval()中创建的任何变量或函数,在严格模式下,为eval 赋值也会导致错误。

window 对象

ECMAScript虽然没有指出如何直接访问Global对象,但Web浏览器都是将这个全局对象作为 window对象的一部分加以实现的。因此,在全局作用域中声明的所有变量和函数,就都成为了window对象的属性。

Math对象

min()和max()方法;

小数值舍入为整数的几个方法:

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

Math.random()方法返回大于等于0 小于1 的一个随机数。

5.面对对象

5.1.属性类型

5.1.1.数据属性

  • [[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特 性,或者能否把属性修改为访问器属性。像前面例子中那样直接在对象上定义的属性,它们的这个特性默认值为 true。
  • [[Enumerable]]:表示能否通过 for-in 循环返回属性。像前面例子中那样直接在对象上定 义的属性,它们的这个特性默认值为 true。 
  • [[Writable]]:表示能否修改属性的值。像前面例子中那样直接在对象上定义的属性,它们的这个特性默认值为 true。 
  • [[Value]]:包含这个属性的数据值。读取属性值的时候,从这个位置读;写入属性值的时候, 把新值保存在这个位置。这个特性的默认值为 undefined。

一般直接在对象上定义的属性,它们的[[Configurable]]、[[Enumerable]] 和[[Writable]]特性都被设置为 true,而[[Value]]特性被设置为指定的值。

要修改属性默认的特性,必须使用ECMAScript 5的Object.defineProperty()方法:

var person = {};
Object.defineProperty(person, "name", {
 writable: false,
 value: "Nicholas"
}); 

5.1.2. 访问器属性

  • [[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特 性,或者能否把属性修改为数据属性。对于直接在对象上定义的属性,这个特性的默认值为 true。 
  • [[Enumerable]]:表示能否通过 for-in 循环返回属性。对于直接在对象上定义的属性,这 个特性的默认值为 true。 
  • [[Get]]:在读取属性时调用的函数。默认值为 undefined。
  • [[Set]]:在写入属性时调用的函数。默认值为 undefined。

访问器属性不能直接定义,必须使用Object.defineProperty()来定义:

var book = {
 _year: 2004,
 edition: 1
};
Object.defineProperty(book, "year", {
 get: function(){
 return this._year;
 },
 set: function(newValue){
 if (newValue > 2004) {
 this._year = newValue;
 this.edition += newValue - 2004;
 }
 }
});
book.year = 2005;
alert(book.edition); //2

_year 前面 的下划线是一种常用的记号,用于表示只能通过对象方法访问的属性。

5.2.定义多个属性

ECMAScript 5 又定义了一个 Object.defineProperties()方法。利用这个方法可以通过描述符一次定义多个属性。这个方法接收两个对象参数:第一个对象是要添加和修改其属性的对象,第二个对象的属性与第一个对象中要添加或修改的属性一一对应。

var book = {};
Object.defineProperties(book, {
 _year: {
 value: 2004
 },

 edition: {
 value: 1
 },
 year: {
 get: function(){ return this._year;
 },
 set: function(newValue){
 if (newValue > 2004) {
 this._year = newValue;
 this.edition += newValue - 2004;
 }
 }
 }
}); 

以上代码在 book 对象上定义了两个数据属性(_year 和 edition)和一个访问器属性(year)。最终的对象与上一节中定义的对象相同。唯一的区别是这里的属性都是在同一时间创建的。

5.3.创建对象

5.3.1.工厂模式

工厂模式抽象了创建具体对象的过程,考虑到在 ECMAScript 中无法创建类,开发人员就发明了一种函数,用函数来封装以特定接口创建对象的细节:

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("Nicholas", 29, "Software Engineer");
var person2 = createPerson("Greg", 27, "Doctor");

函数createPerson()能够根据接受的参数来构建一个包含所有必要信息的Person对象。可以无 数次地调用这个函数,而每次它都会返回一个包含三个属性一个方法的对象。工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。

5.3.2.构造函数模式

6.函数表达式

6.1.闭包与变量

闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数。闭包只能取得包含函数中任何变量的最 后一个值。闭包所保存的是整个变量对象,而不是某个特殊的变量。

function createFunctions(){
 var result = new Array();
 for (var i=0; i < 10; i++){
 result[i] = function(){
 return i;
 };
 }
 return result;
}

上面的函数返回的是10个10,如果想返回1-10的话,可以把var i改成let i。或者创建另一个匿名函数强制让闭包的行为符合预期:

function createFunctions(){
 var result = new Array();
 for (var i=0; i < 10; i++){
 result[i] = function(num){
 return function(){
 return num;
 };
 }(i);
 }
 return result;
} 

匿名函数的执行环境具有全局性,因此其 this 对象通常指向 window

6.2.内存泄漏

function assignHandler(){
 var element = document.getElementById("someElement");
 element.onclick = function(){
 alert(element.id);
 };
}

没搞懂。

6.3.模仿块级作用域

(function(){
 //这里是块级作用域
})(); 

一般这样申明,其实与下面这个作用一样:

var someFunction = function(){
 //这里是块级作用域
};
someFunction(); 

6.4.私有变量

任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外部访问这些变量。 私有变量包括函数的参数、局部变量和在函数内部定义的其他函数。不过闭包可以访问包含它的函数的变量。

我们把有权访问私有变量和私有函数的公有方法称为特权方法。

在对象上创建特权方法的方式:

function MyObject(){
 //私有变量和私有函数
 var privateVariable = 10;
 function privateFunction(){
 return false;
 }
 //特权方法
 this.publicMethod = function (){
 privateVariable++;
 return privateFunction();
 };
} 

对这个例子而言,变量 privateVariable 和函数 privateFunction()只能通过特权方法 publicMethod()来访问。

6.5.静态私有变量

通过在私有作用域中定义私有变量或函数,同样也可以创建特权方法:

(function(){

 //私有变量和私有函数
 var privateVariable = 10;
 function privateFunction(){
 return false;
 }
 //构造函数
 MyObject = function(){
 };
 //公有/特权方法
 MyObject.prototype.publicMethod = function(){
 privateVariable++;
 return privateFunction();
 };
})();