Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript权威指南(第六版)笔记 #7

Open
2 tasks done
deepthan opened this issue Jan 7, 2018 · 0 comments
Open
2 tasks done

JavaScript权威指南(第六版)笔记 #7

deepthan opened this issue Jan 7, 2018 · 0 comments

Comments

@deepthan
Copy link
Owner

deepthan commented Jan 7, 2018

一: 语法结构

1.1 字符集

JavaScript程序是Unicode字符集编写的,Unicode是ASCII和Latin-1的超集。

1.2 区分大小写

js是区分大小写的,HTML不区分大小写。

1.3 标识符

标识符就是一个名字,用来对变量和函数进行命名,或用做JavaScript代码中某些循环语句中的跳转位置的标记。
js的标识符必须以 字母、下划线(_)或美元符($)开始,后续的可以是字母、数字、下划线或美元符。

1.4 关键字&保留字

  • 关键字 :js把一些标识符拿出来用作自己的关键字,因此,就不能再在程序中把这些关键字用作标识符了。
关键字 关键字 关键字 关键字 关键字
break delete function return typeof
case do if switch var
catch else in this void
continue false instanceof throw while
debugger finally new true with
default for null try
  • 保留字:关键字在当前版本没有用到但是在未来版本可能会用到。
    保留字 保留字 保留字 保留字 保留字

class | const | enum | export | extends
import | super | implements | let | private
public | yield | interface | package | protected
static |

  • 严格模式同样对下面的标识符的使用做了限制,它们不能用作变量名、函数名或参数名
name name
arguments eval
  • js预定义了很多全局变量和函数,应当避免把它们的名字用作变量名和函数名
name name name name name
arguments encodeURI Infinity Number RegEXP
Array encodeURIComponent isfinite Object String
Boolean Error isNaN parseFloat SyntaxError
Date eval JSON parseInt TypeError
decodeURI EvalError Math RangeError undefined
decodeURIComponent Function NaN ReferenceError URIError

二:类型、值和变量

js数据类型分为两类:原始类型(primitive type)和对象类型(object type)
  • 原始类型
    包括 数字、字符串、布尔值、null、undefined 。
    变量是直接按值存放的,存放在栈内存中的简单数据段,可以直接访问。
  • 对象(引用)类型
    包括数组、函数、对象。
    存放在堆内存中的对象,变量保存的是一个指针,这个指针指向另一个位置。当需要访问引用类型(如对象,数组等)的值时,首先从栈中获得该对象的地址指针,然后再从堆内存中取得所需的数据。

怎么解释呢?

^-^ 小例子瞬间理解

  • 用钱买东西:其实我们的口袋就像是栈,银行就像是堆,要买的物品就变量,要付的钱就是原始类型或引用类型。
  • 我们买一包辣条(变量)从口袋(栈)拿出钱(原始类型)付款,钱(原始类型)是在口袋(栈)的,付钱速度是非常快的;
  • 如果我们买车(变量)的话也得付钱,这个钱刷银行卡(引用类型),钱在银行(堆),银行卡(引用类型)相当于指针指向你存在银行的大么一大堆钱,不管你在银行存多少钱,你的银行卡大小是固定的。你如果直接带那么多钱(原始类型)放在口袋(栈),会撑爆的~ 所以得带银行卡(引用类型),钱放在银行(堆)。
    ^-^

为变量赋值时,JavaScript的解释程序必须判断该值是原始类型还是引用类型。解析程序则需尝试判断该值是否为JavaScript的原始类型之一。由于这些原始类型占据的空间是固定的,所以可将它们存储在较小的内存区域栈中。这样存储便于迅速查找变量的值。

如果一个值是引用类型,那么它的存储空间会从堆中分配,由于引用值的大小会改变,所以不能将它放在栈中,否则会降低变量查询速度。相反,放在变量的栈空间中的值是该对象存储在堆中的地址,地址的大小是固定的,所以把它存储在栈中对变量性能无任何负面影响。

js数据类型也可以分为可变类型(mutable)和不可变类型(immutable)
  • 可变类型(引用类型内容)
    包括数组、函数、对象。
  • 不可变类型(原始类型内容)
    数字、字符串、布尔值、null、undefined 。
    在许多语言中,字符串都被看作引用类型,而非原始类型,因为字符串的长度是可变的。JavaScript 打破了这一传统。
堆(heap)和栈(stack)区别:

堆和栈都是内存中的一部分,有着不同的作用,而且一个程序需要在这片区域上分配内存。

区别 堆(heap) 栈(stack)
空间分配 大小需要自己申请,并指明大小 系统自动分配释放
存储区 队列优先,先进先出(FIFO—first in first out) 先进后出(FILO—First-In/Last-Out)
缓存方式 二级缓存,生命周期由虚拟机的垃圾回收算法来决定 一级缓存,被调用时处于存储空间中,调用完毕立即释放。
申请效率 由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便. 速度较快。但程序员是无法控制的。
那么问题来了,引用类型怎么比较是否相等呢?

比如两个单独的对象或数组必须比较它们的属性或元素:

function equalArrays(a,b){
    if(a.length != b.length) return false;  //两个长度不同的数组不相等
    for(var i = 0; i<a.length; i++){
        if(a[i] != b[i]) return false;
    }
    return true;
}
趁热理解浅拷贝和深拷贝

对于原始类型如字符串,浅拷贝是对值的复制,对于引用类型如对象来说,浅拷贝是对对象地址的复制。
如前面的买东西的例子:

  • 浅拷贝: 对于买辣条付的钱(原始类型)的复制,是真的钱;而买车是对银行卡(引用类型)的复制,两个卡都指向同一个账户,银行里的同一堆钱,而不是新开了一个账户(堆)。一个卡里面钱用了一点另外一个卡钱有会少。
  • 深拷贝: 买辣条付的钱(原始类型)的复制,是真的钱;而买车是对银行那堆钱(堆)的复制,变成了另外一堆钱,重新开一个账户办个卡(引用类型)对应着它,即使你把新卡钱花完了也不会影响之前的卡。

2.1 数字

在js真实运行环境中, 0.3-0.2 = 0.099 999 999 999 999 98

2.2 转义字符

转义字符 含义
\n 换行符
\f 换页符
\r 回车符
\" 双引号
\' 单引号
\\ 反斜线

2.3 null 和 undefined

  • 对null执行typeof预算,结果返回字符串
  • "object",它表示数字、字符串、对象是“无值的”。
    undefined表示变量没有被初始化,这个属性或元素不存在。它的值是‘未定义’
    它们都表示值得空缺,运算符‘==’ 会认为它们是相等的, '==='可以判断出来。

2.4 全局对象

全局对象属性是全局定义的符号,js程序可以直接使用。

  • 全局属性 : 比如 undefined、Infinity 和 NaN
  • 全局函数 : 比如 isNaN()、parseInt() 和 eval()
  • 构造函数 : 比如 Data()、RegExp()、String()、Object()和 Array()
  • 全局对象 : 比如 Math 和 JSON

2.5 包装对象

存取字符串、数字或布尔值属性时创建的临时对象称为包装对象。
js对象是一种复合值:它是属性或已命名值的集合,通过‘.’符号来引用属性值,当属性值是一个函数的时候,称其为方法。

var str = 'hello world';
var newstr = str.substring(str.indexof(" ")+1, str.length);  // 使用字符串的属性

只要引用了字符串str的属性,JavaScript就会将字符串值通过调用 new String(str) 的方式转换成对象,这个对象继承了字符串的方法,并被用来处理属性的引用,一旦属性引用结束,这个新创建的对象就会被销毁。

数字和字符串可以通过 Number() 和 Boolean()构造函数创建一个临时对象,它们的属性都是可读的,不能给它们定义新的属性。

2.6 不可变的原始值和可变的对象引用

js中的原始值(undefined、null、布尔值、数字和字符串)与对象(数组和函数)有着本质区别:

  • 原始值的比较是值的比较
    原始值是不可更改的,任何方法都无法更改一个原始值。原始值的比较只有当它们的值相等时它们才相等。
var str = 'hello';   //定义一个文本
s.toUpperCase();     // 返回 'HELLO',但是没有改变s的值,它返回的是一个新的字符串的值
console.loh("s的值",s)  // 打印出'hello' ,s的值并没被改变
  • 对象的比较是引用的比较

它们的值是可修改的。

var o = { x : 1 };   // 定义一个对象
o.x  = 2 ;   // 通过修改对象的属性值来更改对象

对象通常称为引用类型(reference type),对象值都是引用,对象的比较均是引用的比较, 当且仅当它们引用同一个基对象时,它们才相等。

var a = [1,2,3];  // 定义一个变量a,引用数组 [1,2,3] 
var b = a;    // 变量b引用同一个数组
b[0] = 4;    // 通过变量b来修改引用的数组
a[0];       // => 4, 变量a也会修改,因为他们引用的是同一个数组
a === b    // true, a和b引用的同一个数组,所以他们相等。

将对象(数组)赋值给变量a,仅仅是赋值的引用值,对象(数组)本身并没有复制一次。

  • 如果想要得到对象或数组的副本,则必须显式复制对象的每个属性或数组的每个元素: 通过循环来完成数组复制
var a = [1,2,3];  // 待复制的数组
var b = [];       // 目标数组
for(var i = 0; i<a.length; i++){  // 遍历 a引用的数组的每个元素
  b[i] = a[i];    // 将元素复制到b中 
}
  • 那么怎么比较数组和对象呢?
    必须比较它们的属性或元素
function equalArrays(a,b){
    if(a.length != b.length) return false;  //两个长度不同的数组不相等
    for(var i = 0; i<a.length; i++){
        if(a[i] != b[i]) return false;
    }
    return true;
}

2.7 类型转换

JavaScript中的取值非常灵活,它将根据需要自行转换类型。

值 --> 转换 字符串 数字 布尔值 对象
undefined "undefined" NaN false throws TypeError
null "null" 0 false throws TypeError
true "true" 1 new Boolean(true)
false "false" 0 new Boolean(false)
""(空字符串) 0 false new String("")
"1.2"(非空数字) 1.2 true new String("1.2")
"one"(非空、非数字) NaN true new String("one")
0 "0" false new Number(0)
[] "" 0 true
  • parseInt(string, radix)
    • radix 为10 ,10进制
      parseInt("10",10) --> 1*10^1 + 0*10^0 = 10
    • radix 为2, 2进制
      parseInt("10",2) --> 1*2^1 + 0*2^0 = 2
    1. 参数
      string : 要被解析的值。如果参数不是一个字符串,则将其转换为字符串(使用 ToString 抽象操作)。字符串开头的空白符将会被忽略。
      radix : 一个介于2和36之间的整数(数学系统的基础),表示上述字符串的基数。比如参数"10"表示使用我们通常使用的十进制数值系统。通常将值默认为10。
    2. 返回值
      返回解析后的整数值。如果被解析参数的第一个字符无法被转化成数值类型,则返回 NaN。
    3. 描述
      把第一个参数转换为字符串并解析它,并返回一个整数或NaN,如果不是NaN,返回的值将是作为指定基数中的数字的第一个参数的整数。
  • parseFloat(string)
    exp : parseFloat("3.14") -- > 3.14
    1. 参数
      string: 需要被解析成为浮点数的字符串.
    2. 描述
      parseFloat是个全局函数,不属于任何对象.将它的字符串参数解析成为浮点数并返回.如果在解析过程中遇到了正负号(+或-),数字(0-9),小数点,或者科学记数法中的指数(e或E)以外的字符,则它会忽略该字符以及之后的所有字符,返回当前已经解析到的浮点数.同时参数字符串首位的空白符会被忽略.
      如果参数字符串的第一个字符不能被解析成为数字,则parseFloat返回NaN.
      你可以通过调用isNaN函数来判断parseFloat的返回结果是否是NaN.如果让NaN作为了任意数学运算的操作数,则运算结果必定也是NaN.
      parseFloat 也可转换和返回Infinity值. 可以使用isFinite 函数来判断结果是否是一个有限的数值 (非Infinity, -Infinity, 或 NaN).

三: 表达式和运算符

3.1 递增 ‘++’、递减‘--’

  1. 当运算符(++)在操作数之前, ++i,对操作数进行增量计算,并返回计算后的值,称为前增量。
  2. 当运算符(++)在操作数之后,i++,它对操作数进行增量运算,但返回的是未做增量计算的值。
var i = 1, j = ++i;  // i = 2, j = 2; 数之前,返回加后的
var i = 1, j = i++;  // i = 2, j = 1; 数之后,返回没加的

‘--’同理。

四:对象

对象是JavaScript基本数据类型。对象是一种复合值,它将很多值复合在一起,可通过名字访问这些值,对象也可以看做是属性的无序集合。属性名是字符串,我们可以把对象那个看成是从字符串到值的映射。它还可以一个称为原型的对象继承属性。对象的方法通常是继承的属性。这种‘原型式继承’是js 的核心特征。

4.1 JavaScript三类对象和两类属性

  • 内置对象(native object)
    是 ECMAScript规范定义的对象或类,例如数组、函数、日期、正则表达式。
  • 宿主对象(host object)
    是有JavaScript解释器所嵌入的宿主环境(比如web浏览器)定义的。客户端JavaScript中表示网页结构的HTMLElement对象均是宿主对象。
  • 自定义对象(user-defined object)
    由运行在JavaScript代码创建的对象。
  • 自有属性(own property)
    直接在对象中定义的属性
  • 继承属性(inherited property)
    在对象的原型对象中定义的属性。

4.2 创建对象

4.2.1 对象直接量
var empty = {}; 
4.2.2 通过new创建对象

new 运算符创建并初始化一个新对象。其后跟随一个函数调用,这个函数称为构造函数。

var empty = new Object();
4.2.3 原型

每一个JavaScript对象(null除外),都和原型相关联,每一个对象都从原型继承属性。
所有通过对象直接量创建的对象都具有同一个原型对象,并可以通过JavaScript代码 Object.prototype获得对原型对象的引用。通过关键词new和构造函数调用创建的对象的原型就是构造函数prototype属性的值。因此同使用{}创建对象一样,通过new Object()创建的对象也继承自 Object.prototype。同样,通过new Array()创建的对象的原型就是Array.prototype,通过new Date() 创建的对象的原型式Date.prototype。

没有原型的对象为数不多,Object.prototype就是其中之一,它不继承任何属性,其他原型对象都是普通对象,普通对象都具有原型。所有的内置构造函数(以及大部分自定义的构造函数)都具有一个继承自 Object.prototype的原型。例如 Date.prototype的属性继承自 Object.prototype,因此由new Date()创建的对象的属性同时继承 Date.prototype和 Object.prototype,这一系列链接的原型对象就是所谓的‘原型链’(prototype chain)。

4.2.4 Object.creat()
var o  = Object.creat({x:1, y:2});

4.3 属性的查询与设置

可以通过点(.)和方括号([])获取属性的值。

// 获取信息
var Name = book.name;
var page = book[total page];

// 设置信息
book.name = '书名';
book[total page] = '200页';

4.4 继承

JavaScript对象具有‘自有属性’(own property),也有一些属性是从原型对象继承而来的。

4.5 删除属性

delete运算符可以删除对象的属性,它的操作数应当是一个属性访问表达式。delete只是断开属性与宿主的联系,而不会去属性中的属性。

delete book.name  // book不再有属性name

delete运算符只能删除自有属性,不能删除继承属性(要删除继承属性必须从定义这个属性的原型对象上删除它)。

4.6 枚举属性

通常使用 for/in循环遍历对象中所有可枚举的属性(包括自有属性和继承属性),把属性名称赋值给循环变量。对象继承的内置方法不可枚举的。但在代码中给对象添加的属性都是可枚举的。
还有两个用以枚举属性的函数:

  • Object.keys()
    它返回一个数组,这个数组由对象中可枚举的自有属性的名称组成。
  • Object.getOwnPropertyNames()
    它返回对象的所有自有属性的名称,而不仅仅是可枚举的属性。

4.7 属性 getter 和 setter

对象属性是由名字、值和一组特性构成,属性可以用一个或两个方法替代: getter/setter。由它们定义的属性称作‘存取器属性’(accessor property),它不同于数据属性(data property),数据属性只有一个简单的值。

和数据属性不同,存取器属性不具有可写性(writable attribute)。如果属性同时具有getter、setter方法,那么它是一个读/写属性。如果它只有getter方法,那么它是一个只读属性;如果只有setter,那么它是一个只写属性,读取只写属性总是返回undefined。

var o = {
    // 普通属性
    data_prop : value,
    
    // 存取器属性都是成对定义的函数
    get accessor_prop() { },
    set accessor_prop(value) {}
}

例子:

var p = {
    // x和y是普通可读写的数据属性
    x : 1,
    y :2,
    
    // r 是可读写的存储器属性,它有 getter 和 setter
    get r() { return this.x + this.y },
    set r(newValue) {
        var oldValue = this.x + this.y;
        var ratio = newValue / oldValue;
        this.x *= ratio;
        this.y *= ratio;
    }
    
    // onlyRead,它只有getter方法
    get onlyRead() { return this.x*this.y; }
}

和数据类型一样,存取器属性是可以继承的,因此可将p当做另一个‘点’的原型。可以给新对象定义它的 x 和 y 属性,但是 r 和 theta 属性是继承来的:

var q = inherit(p); // 创建一个继承getter和setter方法的对象
q.x = 1, q.y = 1;   // 给q添加两个属性
console.log(q.r);  // 可以使用继承的存取器属性
console,log(q.onlyRead)
@deepthan deepthan changed the title JavaScript权威指南(第六版)纪要 JavaScript权威指南(第六版)笔记 Jan 26, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant