Part1.3 ECMAScript和JavaScript及TypeScript
文章内容输出来源:拉勾教育大前端训练营
ECMAScript
- 浏览器中的javascript指的是ES + Web提供的Api(dom bom)
- node环境中javascript指的是ES + node提供的Api
作用域
全局作用域(公共作用域)
1. 在全局作用域中的函数,变量都是公用的.所有相同的变量名称重复命名都会冲突导致报错
2. 函数内部直接赋值变量会把变量提升为全局变量
var a = 3
function a() {
a = 2;
console.log('func a')
}
console.log(a())//TypeError: a is not a function
var a = 3
function test() {
a = 2;
console.log(a)
}
console.log(test())//2
function test() {
console.log(a) //未声明 报错
a = 2;
console.log(a)
}
console.log(test()) //a is not defined
console.log(a)
function test() {
a = 2;
console.log(a) //2
}
console.log(test())
console.log(a) //2
函数作用域
1. 函数内部使用var声明变量(局部变量)只能在函数作用域内使用,外部无法访问到
function test() {
var a = 2;
console.log(a) //2
}
console.log(test())
console.log(a)//a is not defined
块级作用域(ES2015新增)
1. {} 花括号包括的就是块级作用域
2. let 关键字可以绑定到所在的作用域中
3. var 关键字声明的变量会提升到最近的函数作用域当中(函数作用域)
var a=1;
function test() {
function test1() {
if (true) {
var a = 44;
console.log(a) //44
}
console.log(a) //44
}
test1()
console.log(a) //1
}
console.log(a) //1
console.log(test())
console.log(a) //1
const 常量
- 必须有初始值,变量声明过后无法修改,只能修改其中的值 (无法修改的是内存地址,但是存储在内存空间中的数据是可以修改的)
const a = { a: 1 };
a.a= 2
console.log(a) //{ a: 2 }
const b=2;
b=3 //TypeError: Assignment to constant variable.
解构
数组解构
const arr=[1,2,3,4]
const [first,secend,...rest]=arr;
console.log(first)//1
console.log(rest)//[3,4]
对象解构
const obj ={
name:'aa',
age:18
}
const {name,age}=obj
console.log(name)//aa
console.log(age)//18
模板字符串
- 使用 ` ` 声明字符串模板,'${}'插入值
const obj ={
name:'aa',
age:18,
['姓名']:'哈哈'
}
console.log(`字符串输出${obj.姓名}`)//字符串输出哈哈
箭头函数
- 好处:简化代码 , 不会改变this 的指向
- 多个参数用(),多行的函数体使用{} 多行需要自己手动return 返回值
const obj ={
name:'aa',
age:18
}
let age1=obj=>{
return this
}
console.log(age1(obj))//{}
对象字面量
计算属性名
- [ str ] str表达式的计算结果会作为这个对象属性的属性名
const obj ={
name:'aa',
age:18,
['姓名']:'哈哈'
}
let age1=obj=>{
return obj.姓名
}
console.log(age1(obj)) //哈哈
Object.assign 对象拷贝
- 可以将多个对象的原属性复制到目标对象中,如果相同会覆盖目标对象的属性(值传递,不存在引用关系.属于深拷贝)
- Object.assign(targer,...objs) 后面多个obj覆盖到targer目标中
const obj={
name:'test'
}
console.log(Object.assign({},obj)) //{ name: 'test' }
console.log(Object.assign({},obj,obj1)) //{ name: 'test', age: '18' }
Object.is 同值比较
- 同值比较 (区分正负,NaN) === 无法区分的可以用Object.is()
console.log(Object.is(-0,+0)) //false
console.log(Object.is(NaN,NaN)) //true
console.log(-0===+0)//true
console.log(NaN===NaN)//false
Proxy 对象代理器
const obj = {
name: 'test'
}
const newProxy = new Proxy(obj, {
get(targer, property) {
return property in targer ? targer[property] : 'default'
},
set(targer, property, value) {
if (property === "age")
if (!Number.isInteger(value))
throw new TypeError("错误的数据类型")
targer[property] = value
console.log(targer)
}
})
console.log(newProxy.name) //test
console.log(newProxy.xx) //default
newProxy.age = "哈哈" //TypeError: 错误的数据类型
console.log(newProxy.age)
Proxy对比object.defineProperty
- object.defineProperty 只能监测到属性的读写
- Proxy 可以监测到更多
- object.defineProperty 监视属性的变化,需要额外的写很多的属性监视逻辑
const obj = {
name: 'test'
}
const newProxy = new Proxy(obj, {
get(targer, property) {
return property in targer ? targer[property] : 'default'
},
set(targer, property, value) {
if (property === "age")
if (!Number.isInteger(value))
throw new TypeError("错误的数据类型")
targer[property] = value
// console.log(targer)
},
delete(targer,property){
delete targer[property]
}
})
// console.log(newProxy.name)
// console.log(newProxy.xx)
newProxy.age = 18
delete newProxy.age
console.log(newProxy.age) // default
Proxy 对数组的操作监视
- 以非侵入的方式监管了对象的读写
const list = [];
let arrProxy = new Proxy(list, {
set(targer, property, value) {
targer[property] = value
return true //返回写入成功,不写返回值会报错('set' on proxy: trap returned falsish for property '0')
}
})
arrProxy.push(200)
arrProxy.push({ name: 'test' })
console.log(list)
Reflect 一套用于对象操作类(api),13个方法
- Proxy 默认是调用Reflect方法
- 使用带来的好处:统一了操作对象的api
const obj = {
name: 'test'
}
// console.log('name' in obj) //true
// console.log(delete obj['name']) //true
// console.log(Object.keys(obj))//[]
console.log(Reflect.has(obj,'name'))//true
console.log(Reflect.deleteProperty(obj,'name'))//true
console.log(Reflect.ownKeys(obj))//[]
class 类
- 以前的声明类型是用function,使用原型链增加原型方法
- ES6中可以使用class关键字来声明一个类型
- 使用extends关键字继承父类
- 使用super关键字指向继承的父类,访问父类,只能在构造函数中使用,否则会报错(SyntaxError: 'super' keyword unexpected here)
- static 关键字 声明静态方法,无法在内部使用this,指向到当前的类型
function myCls() {
this.name = "测试"
}
myCls.prototype.say = function () {
return this.name
}
let my = new myCls()
console.log(my.say())//测试
class myCls2 extends myCls{
//静态方法
static create(no,name) {
return new myCls2(no,name)
}
constructor(no,name) {
super(name)
this._no = no
}
//实例方法
say() {
return this._no
}
}
// let my2 = new myCls2('测试2')
// console.log(my2.say())//测试2
let my2=myCls2.create(1,'测试2')
console.log(my2.say())
set 数据解构
- 不允许重复的集合对象
- .size() 获取个数
- .has() 判断存在
- .delete() 删除指定元素
- .clear() 清空
- array.from() ... 展开 [ ... ] 得到对象数组
Map 键值对集合
- 对象中的键是string类型的,而Map可以任意类型的值作为键
let m=new Map()
m.set({name:123},90)
console.log(m)
let om=new Object()
om.s=99;
console.log(om)
Symbol
- 以往变量的名称会重复,通过口头协议或者xx_变量名
- 用Symbol的目的是为了定义一种独一无二的属性,用于对象的私有属性
- 用Symbol 函数创建的值一定是唯一的值
- 可以用全局变量和Symbol.for(str) (维护的是str 字符串和Symbol的关系,都是string类型.都会被转换成string类型)
- 使用 for In 循环,object.keys(),JSON.Stringify()都无法拿到用[Symbol()]声明的变量名称
- Object.getOwnPropertySymbols() 拿取的是Symbol类型的属性名称
for of 遍历
- 数据的统一遍历方式 break 终止遍历
- foreach 无法终止遍历
- arr.some() 返回true终止遍历
- arr.every() 返回false 终止遍历
类型安全
强类型和弱类型
- 强类型
- 编译时就会报错 ,错误更早的暴露
- 代码更智能,编码更准确(成员变量 类型不明确 导致编辑器提示不出来,只能自己手写)
- 重构更牢靠 (修改成员变量名称后 无法找到引用的地方,没有编译环节,导致程序无法执行)
- 减少不必要的类型判断
- 弱类型
- 运行时报错
- 各种类型判断,增加代码复杂度
类型系统
- 动态类型语言 变量没有类型 变量的值有类型的区分
类型限制
- 原始类型 string number bool null undefined object Symbol 7种
Flow
- 2014年facabook 推出的工具 用于编码阶段的类型判断,通过类型注解达到类型判断.
- npm i flow-bin 安装
- npm flow init 初始化
- 文件头上需要加上 //@flow
- flow stop 停止命令
- npm flow-remove-types
- npm flow-remove-types .src -d dist 参数一(src) 源代码所在的目录地址 参数二(-d dist) 转换的输出
function (nb :number){
return nb;
}
//数组类型
let arr:Array<number>=[1,2,3] //<> 指定成员类型
let arr:number[]=[1,3,4] //:number[] 效果一样
let arr:[string,number] //元组数据类型(固定长度数组)
//对象类型
let obj:{foo?:string,bar:number}
let obj:{[string]:string} //限制对象属性 key 必须是字符串 ,值也必须是字符串
//函数类型
let fn:(string,number)=>void =function('aa',1){} //指定2个参数类型分别是string 和number,指定无返回值(undefind)
//type 关键字设置类型
type str=string
//字面量类型
type s=string | number //string或者number类型
//maybe 设置可为空
const b: ?number = undefind
const b: number | null | void = undefind
mixed & any 类型
- any 弱类型 编译可以过 但运行未知
- mixed 强类型 不做类型判断编译报错
TypeScript
-
TypeScript 是 JavaScript 的超集,扩展了 JavaScript 的语法
-
string number bool 类型在ts中可以为空(null)值的
-
优点:
- typescript是渐进式的,便于学习
- 注解、类型推断增加了代码的可读性.在编译阶段可以即使发现错误,而不是等到上线阶段
-
缺点:
- 语言增加了很多概念,增加了学习成本
- 要多些一些类型的定义,class对象,对于短期项目增加了成本
-
安装及使用
- npm i typescript -d
- tsc --locale zh-CN 使用中文错误消息(开发不推荐)
- tsc xxx.ts 编译ts文件
- tsc --init 新增配置文件 //成功:message TS6071: Successfully created a tsconfig.json file.
- tsc 根据配置文件运行整个工程文件的ts文件编译
-
断言 数据类型选择 (编译时的类型选择)
let num = 123 as number
let numb = '123' as number //直接报错
/**类型 "string" 到类型 "number" 的转换可能是错误的,因为两种类型不能充分重叠。
如果这是有意的,请先将表达式转换为 "unknown"。
**/
- 接口 类型约束 运行时不存在
//设置动态接口
interface CacheTs {
readonly name: string //只读
[prop: string]: any //属性名string 值可以为任何值
};
let cc: CacheTs = {
name: '张三',
processName: '测试'
};
//cc.name='里斯' //报错无法分配到 "name" ,因为它是只读属性。
console.log(cc.name);//张三
- 枚举
enum book {
'天文',
'地理',
'文学',
'言情',
'科幻'
}
console.log(book[0]) //天文
- 类
- abstract 抽象类 (跟接口相同)
- 不能被实例化
- 继承抽象类的类必须实现抽象类的所有方法
- 访问修饰符
- public 公用
- private 私有
- protected 继承了就等于public没有继承就是private
- 属性修饰符
- readonly 只读
- abstract 抽象类 (跟接口相同)
- 接口
-
只定义,不做具体的实现
- implements 实现接口
- 类实现接口需要实现所有具体的方法
- 不能被实例化
- extends 继承
- 对象继承类,子类的构造函数必须执行一次 super() 函数
- implements 实现接口
-
interface people {
name: string | null | undefined
age: number | undefined
}
class man implements people {
name: string | null | undefined = undefined
age: number | undefined = undefined
sex: number | null | undefined = undefined
constructor(name: string, sex: number, age: number) {
this.name = name;
this.age = age;
this.sex = sex;
}
}
let p = new man("张三", 1, 18)
console.log(p) //man { name: '张三', age: 18, sex: 1 }
javascript 的性能优化
...未完待续