前言
不同语言有不同的范式、思想
了解不同的范式,有助于思路、思维的启发。所谓触类旁通
computer语言始终是工具、武器,而不是拿来争论的谈资,可以为我所用,而不必为之所限
口号: 学习js 为自己赋能
简介
一门非常灵活的 动态 、弱类型 语言
反直觉:
1 性能 2 难度
语言热榜(只是排行和好坏无关)
跑个分:
参考(仅参考不必较真):
https://benchmarksgame-team.pages.debian.net/benchmarksgame/index.html
https://goodmanwen.github.io/Programming-Language-Benchmarks-Visualization/
历史
90年代, Brendan Eich 给 Netscape 开发的浏览器辅助语言 Mocha(后更名为 JavaScript),耗时10天出原型(包含了eval 函数),集成到 Netscape 2预览版里,Mocha 基于对象而非 Java 那样基于类。
Mocha 的动态的对象模型,使用原型链的机制。
Netscape 2 正式版将 Mocha 更名为 JavaScript,后简称 js。
1.0 版本 js 语法大量借鉴 C 语言。行末可不加分号,一开始 js 就是支持的。
1.1版 js 支持隐式类型转换,可以把任意对象转成数字和字符串。1.0版 js 对象不能继承,1.1 加入对象的 prototype 属性,prototype 的属性和实例的对象共享。
为了加到 ISO 标准中,Netscape 找到了 Ecma,因为 ISO 对 Ecma 这个组织是认可的。接着 Ecma 组建了 TC39 技术委员会负责创建和维护 js 的规范。
期间,微软为了能够兼容 js,组建了团队开发 JScript,微软自己摸索然后开发,并写了份规范 The JScript Language Specification, version 0.1 提交给 TC39,微软还将 VBScript 引入 IE。96年 Netscape 为 JavaScript 1.1 写了规范 Javascript 1.1 Specification in Winword format 作为 TC39 标准化 js 的基础。
97年 TC39 发布了 ECMA-262 第一版规范。
Netscape 3 发布后,Brendan Eich 重构了 js 引擎核心,加了嵌套函数、lambda、正则表达式、伪属性(动态访问修改对象)、对象和数组的字面量、基于标记和清除的 GC。lambda 的函数名为可选,运行时会创建闭包,闭包可递归引用自己作为参数。新 js 引擎叫 SpiderMonkey。增加break / continue 标签语句以及 switch。语言升级为 JavaScript 1.2,和 SpiderMonkey 一起集成到 Netscape 4.0。
ES3 结合了 js 1.2 和 JScript 3.0,标准坚持了10年。
2006年 Google 的 Lars Bak 开始了 V8 引擎的开发,2008 基于 V8 的 Chrome 浏览器发布,性能比 SpiderMonkey 快了10倍。
Ryan Dahl 2009 开发了 Node.js ,提供标准库让 js 能够建立 HTTP 服务端应用。此后js就不再局限于前端领域了
ES4 初期目标是希望能够支持类、模块、库、package、Decimal、线程安全等。
这一版本ES4太激进, 很多特性无法通过,而没法通过标准,因此同步设计了10年多的 ES 3.1 最终改名为 ES5
2015年,(ECMAScript 2015)[]发布。这是改动最多的一个版本,加入了大量的语法特性,此后每年发布一版标准
ES6特性
箭头函数、Promise、迭代器....
应用场景
前端、服务端、脚本工具
基础语法
变量声明
const
let
var
类型
number、string、symbol、undefined、null、object、array、function
常见编码规范
switch、for、 if、new
语法参考了C 家族的风格:
条件判断、for循环都需要放到()里
switch需要手动break
for (let i =0 ; i<3; i++){
// do something
}
switch ("some var") {
case "some var":
// logic
break
case "other":
// ...
}
class A{}
const a = new A(); // 符合直觉的创建对象
解构
let obj = {hello:"world"};
let {hello} = obj;
console.log(hello);
let obj2 = {obj};
console.log(obj2);
遍历
for
let arr = [1,2,3];
for (let i = 0; i < arr.length; i++){
console.log(arr[i])
}
for (let i in arr){
console.log(arr[i]);
}
for (let i of arr){ // es6 新增 实现迭代器接口即可 使用此种遍历方式
console.log(i); // 注意此处直接取到value
}
arr.forEach(i=> console.log(i)); // 闭包形式 不支持break、continue
let arrMulti = arr.map(i=>i*i); // 返回值
let obj = {a:"a", b:3};
for (let i in obj){
console.log(i, "value", obj[i]);
}
for (let i of Object.keys(obj)){ // 对象未实现 iterator接口
console.log(i, "value", obj[i]);
}
while、do...while
函数
1 复用代码
2 构造对象
function makeObj(){
this.name = "new obj";
}
let o = new makeObj();
console.log(o)
3 箭头函数
const arrow = (params1, params2) =>{
console.log('abc');
};
// 简写
arrow = (params1, params2) =>console.log('abc');
// 单参数可忽略括号
arrow = params =>console.log('abc');
一般高阶函数的参数(闭包)可用箭头函数
const pro = new Pormise(function(resolve, reject){ resolve('async resolve')});
其次,也可以当做普通函数使用,且不可以当做构造函数
const print = ()=> console.log('hello world');
const obj = new print(); // error
箭头函数没有自己的this
闭包
特征:
1 闭包是(一种特殊的)function
2 闭包一般作为高阶函数的参数或嵌套定义的function
3 闭包[调用态]捕获(感知)所在作用域、作用域外层上下文
js世界中的闭包应用非常广泛
1 js最初是浏览器环境运行的,运行时单线程,早先异步都是通过回调函数来实现,回调函数即最常见的闭包运用场景
request("baidu.com", function(err, response){
if (err){
throw "bad request";
}
console.log(response.status)
})
2 给DOM元素注册监听函数
// 这里是 ctx let someVar = ...
document.addEventListener("foo", event => { // 在 document 上绑定 foo 事件的监听函数
event.preventDefault()
// do something ...
}
}
应用:
// 模拟私有变量
function makeIncrement(){
let __somePrivate = 1; // 此变量外界无法干预
return function(){
// 有内存泄漏嫌疑
return __somePrivate++; // 注意前后缀++运算符区别
}
}
let incementer = makeIncrement();
let count = incrementer(); // 1
count = incrementer(); // 2
语法迷思
js有很多操作其实都是反直觉的
原型链
如果用传统面向对象语言类比,那么原型链是类与类之间的关系,此处需要区别类 和实例
领域关键字
arguments
this:
传统面向对象语言中的this
class MyObj{
constructor(name: string){ // 构造器
this.name = name;
}
say(){
console.log(`my name is ${this.name}`);
}
}
let obj = new MyObj("xiao zhang");
obj.say();
bind、call、apply
slice、substr、substring
this解谜
1 this绑定
四种绑定方式
遵循以下优先级
new绑定 > 显式绑定 > 隐式绑定 > 默认绑定
new
function A(){
this.a = "a";
}
let a = new A();
显式
function Greeting(){
this.name = 'zhansan';
this.greeting = function(){
console.log(`hello ${this.name}`)
}
}
const obj = {name : "wangwu"};
new Greeting().greeting.call(obj);
隐式
const obj = {
name: 'zhangsan',
greeting: function(){
console.log(`hello ${this.name}`)
}
}
obj.greeting();
const obj2 = {name:"aaa"};
obj2.hello = obj.greeting;
obj2.hello();
默认
其他方式都算做默认绑定
2 箭头函数this
1 不准确的说法
箭头函数指向外层
箭头函数this始终不变
let obj = {
a: ()=> console.log(this),
};
obj.a();
obj = function(){
this.a = ()=> console.log(this);
}
new obj().a();
obj = {
a: {
b: ()=> console.log(this),
},
};
obj.a.b();
obj = {
a: function(){
return ()=> console.log(this);
},
};
obj.a()();
// 箭头函数this始终指向外层【函数】所绑定的作用域
// 箭头函数this始终不变 是错误的说法
let b = {b:"b"};
b.a = obj.a;
b.a()();
obj = {
a: function(){
this.hello = 'world';
this.b = ()=> console.log(this);
},
};
new obj.a().b();
3 规则
this的值在函数被调用的时候才会指定
应用解答
前置知识
var关键字
1 挂载到window对象
2 变量不经申明也挂载到window对象下
坑点: 全局变量污染
function b(){
c = 18;
}
b();
console.log(c);
3 没有块级别作用域
if (true){
var hello = 'world';
}
console.log(hello);
var和let区别
1 不挂载window
2 有块级别作用域
var number = 5;
var obj = {
number: 3,
fn1: (function () {
var number; // undefined
this.number *= 2; // 10
number = number * 2; // NaN
number = 3; // bibao.number = 3
return function () {
// this == obj
var num = this.number; // num = 10 、3
this.number *= 2; // this.number = 20 、6
console.log(num); // print 10 3
number *= 3; // bibao.number = 9 、27
console.log(number); //print 9 27
}
})()
}
var fn1 = obj.fn1;
fn1.call(null); // window.fn1()
obj.fn1();
console.log(window.number); // 20
Promise API
| 天不生ES6 JS万古如长夜
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。
构造函数
// 主意构造方法 比较独特
const myPromise = new Promise((resolve, reject)=>{
if (// 异步操作成功) resolve('success');
reject('error');
})
// then 方法接收两个函数 分别是成功和失败的处理
myPromise.then(()=> console.log('hello world'))
此时 myPromise
是一个thenable 对象, 且通过then方法返回的也是 promise对象
特点
对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending
(进行中)、fulfilled
(已成功)和rejected
(已失败)。
一旦状态改变,就不会再变,任何时候都可以得到这个结果。
实例方法
所有promise 对象的实例方法均会返回promise对象,从而可以形成调用链
const obj = new Promise(function(reslove, reject){
// do somthing
resolve();
})
obj.then(()=> consolt.log('hello world')).
catch(e=> console.log(e)).finally(()=>console.log('over'));
// then 方法接收两个参数
obj.then((rst)=>console.log('success with result ', rst),
err => console.log(err)
)
// catch 是 then(null, rejection)的快捷方式 一般推荐catch方式处理失败情况
obj.then((rst)=>console.log('success with result ', rst)).
catch(err => console.log(err));
// catch 可以处理 构造函数里 reject 抛出的错误
// 也可以处理 resolve或 then 里通用代码抛出的异常
obj.then(()=>{
throw new Error('err')
}).catch((e)=>console.log(`catch error ${e}`));
// 此时如果没有catch 的话则会抛出运行时异常
静态方法
Promsie.all
const p = Promise.all([p1, p2, p3]);
Promise.all()方法接受一个数组作为参数,p1、p2、p3都是 Promise 实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。另外,Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。
p的状态由p1、p2、p3决定,分成两种情况。(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
Promise.race
Promise.allSettled
Promise.resolve
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
// resolve方法相当于一个快捷方式
resolve的参数:
1 promise对象则原封不动返回
2 thenable 对象则立即执行then方法然后返回结果对象的promise包装
3 其他则包装参数中内容为 resolve状态的promise对象
何谓thenable
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};