homewo day3预习
q:链表存在的意义:把不连续的内存(比如:list)连起来,或者把两个离得远的内存地址连起来。
1.JS数据类型和数据结构
数据类型:最新的 ECMAScript 标准定义了 8 种数据类型:
7种原始类型,除 Object 以外的所有类型都是不可变的(值本身无法被改变)。
用typeOf运算符检查:
Number:JavaScript 中只有一种数字类型,标准的双精度 64 位二进制格式的值(-(253 -1) 到 253 -1)。它并没有为整数给出一种特定的类型。除了能够表示浮点数外,还有一些带符号的值:+Infinity,-Infinity 和 NaN (非数值,Not-a-Number)。
要检查值是否大于或小于 +/-Infinity,你可以使用常量 Number.MAX_VALUE 和 Number.MIN_VALUE。另外在 ECMAScript 6 中,你也可以通过 Number.isSafeInteger() 方法还有 Number.MAX_SAFE_INTEGER 和 Number.MIN_SAFE_INTEGER 来检查值是否在双精度浮点数的取值范围内。
Boolean:布尔表示一个逻辑实体,可以有两个值:true 和 false。
** String**:16位的无符号整数值的“元素”。
获取一个字符串的子串可通过选择个别字母或者使用 String.substr(). 两个字符串的连接使用连接操作符 (+) 或者 String.concat().
undefined :一个没有被赋值的变量会有个默认值 undefined
null:Null 类型只有一个值: null
Bigint
Symbol
对象类型,用instanceOf关键字检查:
记住 typeof 操作符的唯一目的就是检查数据类型,检查任何从 Object
派生出来的结构类型,使用 typeof 是不起作用的,因为总是会得到 “object”。检查 Object 种类的合适方式是使用nstanceof 关键字。
在 JavaScript 里,对象可以被看作是一组属性的集合。用 对象字面量语法 来定义一个对象时,会自动初始化一组属性。(也就是说,你定义一个var a = “Hello”,那么a本身就会有a.substring这个方法,以及a.length这个属性,以及其它;如果你定义了一个对象,var a = {},那么 a 就会自动有 a.hasOwnProperty 及 a.constructor 等属性和方法。)而后,这些属性还可以被增减。
ECMAScript 定义的对象中有两种属性:数据属性和访问器属性。
2.let
let 语句声明一个块级作用域的本地变量,并且可选的将其初始化为一个值。
看第四条
作用域规则:
let声明的变量只在其声明的块或子块中可用,这一点,与var相似。二者之间最主要的区别在于var声明的变量的作用域是整个封闭函数。
模仿私有成员:
在处理构造函数的时候,可以通过let声明而不是闭包来创建一个或多个私有成员。
重复声明:
在同一个函数或块作用域中重复声明同一个变量会引起SyntaxError。
然而,需要特别指出的是,一个嵌套在 case 子句中的块会创建一个新的块作用域的词法环境,就不会产生上诉重复声明的错误。
暂存死区:
与通过 var 声明的有初始化值 undefined 的变量不同,通过 let 声明的变量直到它们的定义被执行时才初始化。在变量初始化前访问该变量会导致 ReferenceError。该变量处在一个自块顶部到初始化处理的“暂存死区”中。
暂存死区与 typeof:
与通过var声明的变量, 有初始化值 undefined和只是未声明的变量不同的是,如果使用typeof检测在暂存死区中的变量, 会抛出ReferenceError异常
其他情况:
用在块级作用域中时, let将变量的作用域限制在块内, 而var声明的变量的作用域是在函数内.
3.函数
定义函数
一个函数定义(也称为函数声明,或函数语句)
- 函数的名称。
- 函数参数列表,包围在括号中并由逗号分隔。
- 定义函数的 JavaScript 语句,用大括号{}括起来。
function square(number) {
return number * number;
}
函数表达式
这样的函数可以是匿名的。
const square = function(number) {
return number * number;
};
var x = square(4); // x gets the value 16
调用函数
定义一个函数并不会自动的执行它。定义了函数仅仅是赋予函数以名称并明确函数被调用时该做些什么。调用函数才会以给定的参数真正执行这些动作。
函数声明
只有使用语法形式(function funcName(){})才可以。而下面的代码是无效的。就是说,函数提升仅适用于函数声明,而不适用于函数表达式。
console.log(square); // square is hoisted with an initial value undefined.
console.log(square(5)); // Uncaught TypeError: square is not a function
递归
函数可以被递归,比如:计算阶乘
function factorial(n){
if ((n == 0) || (n == 1))
return 1;
else
return (n * factorial(n - 1));
}
与函数可以指向并调用自身。
三种方法:
- 函数名
- arguments.callee
- 作用域下的一个指向该函数的变量名
eg:
var foo = function bar() {
// statements go here
};
在这个函数体内,以下的语句是等价的:
bar()
arguments.callee() (ES5禁止在严格模式下使用此属性)
foo()
函数作用域
作用域和函数堆栈
将递归算法转换为非递归算法是可能的,不过逻辑上通常会更加复杂,而且需要使用堆栈。事实上,递归函数就使用了堆栈:函数堆栈。
堆就是n叉树,堆是用来遍历的 栈是用来返回的 一个函数要执行完了才结束,或者中间有return 才结束
嵌套函数和闭包
在一个函数里面嵌套另外一个函数。嵌套(内部)函数对其容器(外部)函数是私有的。它自身也形成了一个闭包。
一个闭包是一个可以自己拥有独立的环境与变量的表达式(通常是函数)。
既然嵌套函数是一个闭包,就意味着一个嵌套函数可以”继承“容器函数的参数和变量。换句话说,内部函数包含外部函数的作用域。
- 内部函数只可以在外部函数中访问。
- 内部函数形成了一个闭包:它可以访问外部函数的参数和变量,但是外部函数却不能使用它的参数和变量
内部函数形成了闭包,因此你可以调用外部函数并为外部函数和内部函数指定参数:
function outside(x) {
function inside(y) {
return x + y;
}
return inside;
}
fn_inside = outside(3); // 可以这样想:给一个函数,使它的值加3
result = fn_inside(5); // returns 8
result1 = outside(3)(5); // returns 8
保存变量
一个闭包必须保存它可见作用域中所有参数和变量。因为每一次调用传入的参数都可能不同,每一次对外部函数的调用实际上重新创建了一遍这个闭包。只有当返回的 inside 没有再被引用时,内存才会被释放。
多层嵌套函数
函数可以被多层嵌套。例如,函数A可以包含函数B,函数B可以再包含函数C。B和C都形成了闭包,所以B可以访问A,C可以访问B和A。因此,闭包可以包含多个作用域;他们递归式的包含了所有包含它的函数作用域。这个称之为作用域链。
命名冲突
当同一个闭包作用域下两个参数或者变量同名时,就会产生命名冲突。更近的作用域有更高的优先权。
这就是作用域链。链的第一个元素就是最里面的作用域,最后一个元素便是最外层的作用域。
闭包
闭包
闭包是 JavaScript 中最强大的特性之一。JavaScript 允许函数嵌套,并且内部函数可以访问定义在外部函数中的所有变量和函数,以及外部函数能访问的所有变量和函数。但是,外部函数却不能够访问定义在内部函数中的变量和函数。这给内部函数的变量提供了一定的安全性。
此外,由于内部函数可以访问外部函数的作用域,因此当内部函数生存周期大于外部函数时,外部函数中定义的变量和函数的生存周期将比内部函数执行时间长。当内部函数以某一种方式被任何一个外部函数作用域访问时,一个闭包就产生了。
外部函数的变量对内嵌函数来说是可取得的,而除了通过内嵌函数本身,没有其它任何方法可以取得内嵌的变量。内嵌函数的内嵌变量就像内嵌函数的保险柜。它们会为内嵌函数保留“稳定”——而又安全——的数据参与运行。而这些内嵌函数甚至不会被分配给一个变量,或者不必一定要有名字。
尽管有上述优点,使用闭包时仍然要小心避免一些陷阱。如果一个闭包的函数定义了一个和外部函数的某个变量名称相同的变量,那么这个闭包将无法引用外部函数的这个变量。
arguements类数组对象
arguments变量只是 ”类数组对象“,并不是一个数组。称其为类数组对象是说它有一个索引编号和length属性。尽管如此,它并不拥有全部的Array对象的操作方法。
函数的实际参数会被保存在一个类似数组的arguments对象中。找出传入的参数:
参数的数量由arguments.length
arguments[i]
函数参数
ECMScript6,出现新的参数:默认参数、剩余参数
过去用函数体的检查:
默认参数
b = (typeof b !== 'undefined') ? b : 1;
现在可以在函数头简单地把1设定为b的默认值:
function multiply(a, b = 1) {
return a*b;
}
multiply(5); // 5
剩余参数
剩余参数语法允许将不确定数量的参数表示为数组。
箭头函数
箭头函数表达式(也称胖箭头函数)相比函数表达式具有较短的语法并以词法的方式绑定 this。箭头函数总是匿名的。
参考
this 的词法
在箭头函数出现之前,每一个新函数都重新定义了自己的 this 值(在构造函数中是一个新的对象;在严格模式下是未定义的;在作为“对象方法”调用的函数中指向这个对象;等等)。
预定义函数
eval()
eval()方法会对一串字符串形式的JavaScript代码字符求值。
isFinite()
isFinite()函数判断传入的值是否是有限的数值。 如果需要的话,其参数首先被转换为一个数值。
isNaN()
isNaN()函数判断一个值是否是NaN。注意:isNaN函数内部的强制转换规则十分有趣; 另一个可供选择的是ECMAScript 6 中定义Number.isNaN() , 或者使用 typeof来判断数值类型。
parseFloat()
parseFloat() 函数解析字符串参数,并返回一个浮点数。
parseInt()
parseInt() 函数解析字符串参数,并返回指定的基数(基础数学中的数制)的整数。
encodeURI()/encodeURIComponent()
encodeURI()方法通过用以一个,两个,三个或四个转义序列表示字符的UTF-8编码替换统一资源标识符(URI)的某些字符来进行编码(每个字符对应四个转义序列,这四个序列组了两个”替代“字符)
decodeURI()/decodeURIComponent()
decodeURI() 函数对先前经过encodeURI函数或者其他类似方法编码过的字符串进行解码。
4.JS对象基础
JavaScript (“一切皆对象most things are objects”)
对象:是一个包含相关数据和方法的集合,由一些变量和函数组成,我们称之为属性和方法。
点表示法(dot notation)
来访问对象的属性和方法。
person.age
person.interests[1]
person.bio()
子命名空间
name : {
first : 'Bob',
last : 'Smith'
},
name : {
first : 'Bob',
last : 'Smith'
},
括号表示法
person['age']
person['name']['first']
设置对象成员
person.age = 45
person['name']['last'] = 'Cratchit'
也可以创建新的成员
person['eyes'] = 'hazel'
person.farewell = function() { alert("Bye everybody!") }
5.使用对象
- JS的设计是基于对象的范式,一个对象是一系列属性的集合,一个对象可以是一个拥有属性和类型的实体,在现实世界里比如:杯子。
- 对象中未赋值的属性的值为undefined(而不是null)。
- 象的属性也可以通过方括号访问或者设置
myCar["make"] = "Ford";
注意方括号中所有键都会转换成String,因为JavaScript中的对象只能使用String类型作为键类型。
从 ECMAScript 5 开始,枚举一个对象的所有属性
三种原生方法:
- for…in :依次访问一个对象及其原型链中所有可枚举的属性
- Object.keys(o): 返回对象o自身包含(不包含原型)的所有可枚举的属性的数组
- Object.getOwnPropertyNames(o):返回对象 o 自身包含(不包括原型中)的所有属性(无论是否可枚举)的名称的数组。
创建新对象
- 使用对象初始化器
var obj = { property_1: value_1, // property_# 可以是一个标识符...
2: value_2, // 或一个数字...
["property" +3]: value_3, // 或一个可计算的key名...
// ...,
"property n": value_n }; // 或一个字符串
- 使用构造函数:
- 创建一个构造函数来定义对象的类型。首字母大写。
- 通过 new 创建对象实例。
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
var mycar = new Car("Eagle", "Talon TSi", 1993);
一个对象的属性值可以是另一个对象。可以为之前定义的对象增加新的属性。
3. 使用 Object.create 方法:它允许你为创建的对象选择一个原型对象,而不用定义构造函数。
// Animal properties and method encapsulation
var Animal = {
type: "Invertebrates", // 属性默认值
displayType : function() { // 用于显示type属性的方法
console.log(this.type);
}
}
// 创建一种新的动物——animal1
var animal1 = Object.create(Animal);
animal1.displayType(); // Output:Invertebrates
// 创建一种新的动物——Fishes
var fish = Object.create(Animal);
fish.type = "Fishes";
fish.displayType(); // Output:Fishes
继承
所有的 JavaScript 对象至少继承于一个对象。被继承的对象被称作原型,并且继承的属性可通过构造函数的 prototype 对象找到。
通过 this 引用对象
this 在一个方法中指调用的对象。当与 form 属性一起使用时,this 可以指代当前对象的父窗体。
定义 getters 与 setters
一个 getter 是一个获取某个特定属性的值的方法。
一个 setter 是一个设定某个属性的值的方法。
var o = {
a: 7,
get b() {
return this.a + 1;
},
set c(x) {
this.a = x / 2
}
};
console.log(o.a); // 7
console.log(o.b); // 8
o.c = 50;
console.log(o.a); // 25
原则上,getter 和 setter 既可以:
- 使用对象初始化器 定义:只需要在getter方法前加get,在setter方法前加set,当然,getter方法必须是无参数的,setter方法只接受一个参数(设置为新值),例如:
var o = {
a: 7,
get b() { return this.a + 1; },
set c(x) { this.a = x / 2; }
};
- 也可以之后随时使用 getter 和 setter 添加方法添加到任何对象
var o = { a:0 }
Object.defineProperties(o, {
"b": { get: function () { return this.a + 1; } },
"c": { set: function (x) { this.a = x / 2; } }
});
o.c = 10 // Runs the setter, which assigns 10 / 2 (5) to the 'a' property
console.log(o.b) // Runs the getter, which yields a + 1 or 6
删除属性
你可以用 delete 操作符删除一个不是继承而来的属性.
delete myobj.a;
如果一个全局变量不是用 var 关键字声明的话,你也可以用 delete 删除它.
g = 17;
delete g;
比较对象
在 JavaScript 中 objects 是一种引用类型。两个独立声明的对象永远也不会相等,即使他们有相同的属性,只有在比较一个对象和这个对象的引用时,才会返回true.
// 两个变量, 两个具有同样的属性、但不相同的对象
var fruit = {name: "apple"};
var fruitbear = {name: "apple"};
fruit == fruitbear // return false
fruit === fruitbear // return false
// 两个变量, 同一个对象
var fruit = {name: "apple"};
var fruitbear = fruit; // 将fruit的对象引用(reference)赋值给 fruitbear
// 也称为将fruitbear“指向”fruit对象
// fruit与fruitbear都指向同样的对象
fruit == fruitbear // return true
fruit === fruitbear // return true