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 只读
  • 接口
    • 只定义,不做具体的实现

      • implements 实现接口
        • 类实现接口需要实现所有具体的方法
        • 不能被实例化
      • extends 继承
        • 对象继承类,子类的构造函数必须执行一次 super() 函数
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 的性能优化

...未完待续

上一篇:实现JavaScript的三个部分


下一篇:跟我一起学javascript(1) javascript简介