前言
之前在前端方面的学习都很随意,零零散散的。现在打算系统地把知识全部梳理一遍,特此记录。
第一篇,整理一些JS的语言基础相关知识。
HTML中的JavaScript
Javascript = ECMAScript + DOM + BOM。
JavaScript由<script>元素引入HTML页面。所有元素会依照它们在网页中出现的次序被解释。
一些属性:
- defer:把脚本推迟到文档渲染完毕后再执行。
- async:实现异步加载。
语言基础
严格模式的启用:
use strict
变量关键字
var(别用)函数作用域。声明(hoist)的变量自动提升到作用域顶部,可重复声明。let(次之) 块作用域。不会在作用域中提升(导致暂时性死区),不允许冗余声明。const(优先)和let基本相同。声明变量时必须同时初始化。修改会报错。
for(var i = 0;i < 5;i++){
setTimeout(() => console.log(i), 0);
}
//5, 5, 5, 5, 5
for(let i = 0;i < 5;i++){
setTimeout(() => console.log(i), 0);
}
//0, 1, 2, 3, 4数据类型(6简单,1复杂)
- Undefined: 值只有
undefined。未初始化。(null的派生) - Null: 值只有
null。空对象指针。建议使用null来初始化保存对象值的变量。
console.log(null == undefined); //true- Boolean:
true, false。不同类型的值都可以根据不同的规则转化为布尔值。
| 数据类型 | 转为true | 转为false |
|---|---|---|
| Boolean | true | false |
| String | 非空字符串 | 空字符串 |
| Number | 非零数值 | 0、NaN |
| Object | 任意对象 | null |
| Undefined | N/A(不存在) | undefined |
Number: 整数、浮点值都可表示。
- 数值范围超限的时候,转化为
Infinity,无穷用isFinite()判断。数值操作错误时为NaN,用isNaN()判断。 - 数值转换:
parseInt(), parseFloat() Number()转换的规则
- 数值范围超限的时候,转化为
String: 字符串。
toString()转换成字符串。可用于数值、布尔、对象、字符串。(null和undefined没有)- 字符字面量
- 模板字面量(ES6新增) 保留换行符,可以跨行。
- 字符串插值
- Symbol(ES6): 唯一,不可变。确保对象属性使用唯一标识符。
- Object: 所有对象的基类。拥有一些属性和方法。
操作符
== 和 ===
变量、作用域与内存
两种数据
原始值、引用值。
执行上下文与作用域
任何变量都存在于某个执行上下文中(作用域)。上下文的代码在执行的时候,会创建变量对象的一个作用域链。
内部上下文可以通过作用域链访问外部上下文中的一切,但外部上下文无法访问内部上下文中的任何东西。(线性的、有序的)
作用域链的增强
- try/catch语句中的catch块
- with语句
变量声明的作用域
varlet
垃圾回收
- 标记清理
- 引用计数
- 内存管理
基本引用类型
Date
不传参的情况下,保存当前日期和时间。基于其他日期,需要传入毫秒表示。
Date.parse()Date.UTC()
RegExp
借此支持正则表达式。
原始值包装类型
每当用到某个原始值的方法或属性的时候,后台都会创建一个相应原始包装类型的对象,从而暴露出操作原始值的各种方法。可以让原始值拥有对象的行为。
涉及原始值的语句执行完毕后,包装对象就会被销毁。
Boolean(别用)
let falseValue1 = new Boolean(false); //Boolean对象 let result1 = falseObject1 && true; console.log(result1); //true let falseValue2 = false; //原始布尔值 let result2 = falseObject2 && true; console.log(result2); //false
result1的赋值表达式中,是对falseObject对象而不是对它的值求值。(所有对象的布尔值都是true)
- Number
String
字符串操作方法
concat()slice()substr()substring()
字符串位置方法
indexOf()lastIndexOf()
字符串包含方法
startsWith()endsWith()includes()
trim()创建字符串副本、删除前后所有空格符合- 其他方法..(大小写、字符串匹配)
迭代与解构
@@iterator
内置单例对象
Global
一些方法
encodeURI()不编码特殊字符和encodeURIComponet()编码非标准字符decodeURI()和decodeURIComponet()解码eval()一个完整的ECMAScript解释器!定义的任何变量和函数都不会被提升。
一些对象
- window
- ...
Math
min()max()random()
集合引用类型
Object
最好对必选参数使用命名参数,再通过一个对象字面量来封装多个可选参数。
- 构造方式:Object构造函数或对象字面量。
Array
数组中每个槽位可以存储任意类型的数据。
- 构造方式:Array构造函数或数组字面量。
- (ES6):
from()将数组结构转换成数组实例和of()将一组参数转换为数组示例 - 栈方法、队列方法、排序方法、操作方法...
Map(ES6)
Map实例会维护键值对的插入顺序。
| Object | Map | |
|---|---|---|
| 内存占用 | 对于给定固定大小的内存,Map大约可以比Object多存储50%的键值对。 | |
| 插入性能 | 插入新键值对,消耗大致相当。 | 涉及大量插入操作,Map更佳。 |
| 查找速度 | 涉及大量查找操作,某些情况Object更佳。 | 性能差异极小。 |
| 删除性能 | 涉及大量删除操作,Map更佳。 |
WeakMap
弱映射中的键不属于正式的引用,不会阻止垃圾回收。
Set
Set实例会维护键值对的插入顺序,支持顺序迭代。可以包含任何JS数据类型作为值。
WeakSet
弱集合中的键不属于正式的引用,不会阻止垃圾回收。
面向对象编程
创建对象
工厂模式
function createPerson(name,age,job){ let o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function(){ console.log(this.name); } return o; }
构造函数模式
function Person(name,age,job){ this.name = name; this.age = age; this.job = job; this.sayName = function(){ console.log(this.name); } } let person1 = new Person("Nicholas", 29 , "software engineer"); let person2 = new Person("Greg", 27 , "doctor");
原型模式
每个函数都会创建一个prototype属性。
实例与构造函数原型之间有直接的联系,但实例与构造函数之间没有。
function Person() {} Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.jog = "doctor"; Person.prototype.sayName = function(){ console.log(this.name); }; let person1 = new Person(); person1.sayName();// "Nicholas" let person2 = new Person(); person2 .sayName();// "Nicholas"
对象字面量重写原型:
function Person() {}
Person.prototype = {
name:"Nicholas",
age:29,
job:"doctor",
sayName(){
console.log(this.name);
}
}如果在实例上添加一个与原型对象中同名的属性,那这个实例上就会创建一个新属性,遮蔽原来的属性。
person1.name = "Greg";
person1.sayName();// "Greg"可以使用delete删除。
delete person1.name;
person1.sayName();// "Nicholas"继承
原型链:通过原型继承多个引用类型的属性和方法。
- 以对象字面量方式创建原型方法会破坏之前的原型链!
问题
- 原型中包含的引用值会在所有实例间共享。
- 子类在实例化的时候不能给父类的构造函数传参。
盗用构造函数:在子类构造函数中构造父类函数。
- 可以在调用父类构造函数之后再给子类实例添加额外的属性。
问题
- 必须在构造函数中定义方法,因此函数不能重用。
*组合继承:使用原型链继承原型上的属性和方法,而通过盗用构造函数继承实例属性。
function SuperType(name){ this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function(){ console.log(this.name); } function SubType(name, age){ SuperType.call(this.name);//盗用构造函数继承实例属性 this.age = age; } SubType.prototype = new SuperType();//原型链继承方法
原型式继承:即使不自定义类型也可以通过原型实现对象之间的信息共享。
- 适合不单独创建构造函数、但需要在对象间共享信息的场合。
寄生式继承:创建一个实现继承的函数,以某种方式增强对象,然后返回。
- 适合主要关注对象,而不在乎类型和构造函数的场景。
- 会导致函数难以重用。
function createAnother(original){ let clone = object(original); clone.sayHi = function(){ console.log("hi"); }; return clone; }
寄生式组合继承:通过盗用构造函数继承属性,使用混合式原型链继承方法。/寄生式继承继承父类原型,将返回的新对象赋值给子类原型。
function inheritPrototype(subType, superType){ let prototype = object(superType.prototype);//创建对象 prototype.constructor = subType;//增强对象 subType.prototype = prototype;//赋值对象 }
类
类的定义不能提升,类受块作用域限制。
构造函数
- 调用类构造函数必须使用
new操作符。
- 调用类构造函数必须使用
- 实例、原型、类成员
继承:原型链
- 使用
extends关键字,可以继承一个类或者普通的构造函数。 派生类的方法可以通过
super关键字引用他们的原型。(只能在派生类中使用super)super使用的注意事项- 只能在派生类构造函数和静态方法中使用。
- 不能单独使用。
- 会调用父类构造函数,并将返回的实例赋值给
this。 - 类构造函数中,不能在调用
super()之前引用this。 - 派生类中若显式定义构造函数,那必须在其中调用
super(),要么必须在其中返回一个对象。
- 抽象基类:
new.target实现。 - 可以继承内置类型
- 使用


