js前端技术
一、前端技术
1、HTML
HTML(hypertext markup language)超文本标记语言,不同于编程语言。
超文本就是超出纯文本的范畴,描述文本的颜色、大小、字体。
HTML由一个个标签组成,标签各司其职,有的提供网页信息,有的负责图片,有的负责网页布局。
超文本需要显示,就得有软件呈现超文本定义的排版格式,,例如显示图片、表格、显示字体的大小,颜色,软件就是浏览器。
超文本的诞生是为了解决纯文本不能格式显示问题,是为了好看,但是只用通过网络分享超文本的内容,所以制定了HTTP协议。
2、浏览器
1)历史、1980年代,tim berners-Lee为cern设计基于超文本思想的enquire,以促进科研人员之间信息更新和共享。19899年其编写了《信息化管理;建议》一文,并构建基于Internet的hypertext系统,并在cern开发了world wide web项目。打造了世界上第一站。于1991年8月6日上线。
Tim berners-lee于1990年发明了第一个浏览器,还发明了HTTP协议。
1994年MIT创建了w3c。w3c万维网联盟,负责万维网持续发展,提出w3c的标准应该基于无专利权、无版税。
Marc Andreessen于1993年发明了mosaic浏览器,看到了技术前景,不久后成立自己的公司---网景公司Netscape,1994发不了Netscape navigator浏览器,席卷全球。
1995年微软发布IE。
1999年网景被aol收购,收购后,Netscape公开了浏览器代码。创建了Mozilla组织。Mozilla组织使用gecko引擎重写浏览器。。
2003网景被解散。
2008年google的Chrome浏览器待着v8引擎横空出世。
2)网景公司
HTTP cookie,解决HTTP无状态。
Javascript
Ssl协议:
Jar格式文件,将Java的class文件打包压缩,并加上签名。
2012年4月9日,微软购买800项美国在线的专利或专利授权。
3)浏览器技术
浏览器是特殊的客户端。
浏览器软件分为两个部分
外壳:
外壳提供用户交互的界面。
内核(引擎engine)
提供HTML,css,图像的渲染引擎。提供DOM编程接口。
提供Javascript引擎。
排版(渲染)引擎 |
浏览器 |
说明 |
Gecko |
Firefox |
|
Trident |
IE,AOL |
|
Khtml |
|
|
Presto |
oPera |
|
Webkit |
Safari,Chrome |
|
Blink |
Chrome,Opera |
|
Js引擎:
Jscript、tracemonkey(firefox) v8等
使用jquery等框架来解决兼容性问题。
3、js
是一种动态的弱类型的脚本解释性语言。和HTML、css秉承三大web核心技术。
4、ES
ECMAscript:是ecma国际组织
JavaScript是商品名。
2009年 ES5发布
2015年ES6发布
5、v8引擎
谷歌推出的,使用的是bsd协议开源。
二、nodejs
1、nodejs简介
Nodejs是服务器端运行JavaScript的开源、跨平台运行环境。
作者是瑞安达尔(ryan dahl),2009年发布,使用了v8引擎,采用时间驱动,非阻塞,异步IO模型。
2012年,npm软件包管理器诞生,通过其,可以方便的发布,分享nodejs的库和源代码。
Nodejs4.0引入了ES6语言的特性。
2、安装
国内阿里云镜像
https://npm.taobao.org/mirrors/node
Linux:
https://npm.taobao.org/mirrors/node/latest-v8.x/node-v8.11.3-linux-x64.tar.xz
windows:
https://npm.taobao.org/mirrors/node/latest-v8.x/node-v8.11.3-x64.msi
默认路径安装:
3、开发
Visual studio code
https://code.visualstudio.com/Download
4、注释
和c、Java一样
//单行注释
/*comment*/ 多行注释,在语句中间使用。
str = 'hello' + /*comment*/ 'student'
5、常量和变量
标识符
标识符必须是字母、下划线、美元符号和数字,但必须是字母、下划线、美元符号开头。,不能是数字开头。
标识符区分大小写。
声明
var a声明 a值为undefined 声明全局变量
let b 声明 let 块变量,局部变量。
const c常量声明时候必须赋值。后期不允许更改。明确知道一个标识符定以后不在膝盖,声明的时候使用const常量,减少被修改的风险。
能给常量就不用变量。
变量和常量声明和初始化的过程中是可以分开的。
var a
let b
console.log(a,b)
a = 1
b = 'a string'
console.log(a,b)
//const c //不能定义,因为const定义时候必须赋值,之后不可以再次进行更改
const c = 100
console.log(c)
var y //只是复制,y的值为undefined
var x = 1 //规范的声明并初始化,声明全局或局部变量
function hello()
{
var a //只是声明,a为undefined,作用域是在函数中
a = 100 //赋值
}
//console.log(2,a) //抛出错误,变量a未定义
//a = 200 //不能提升作用域
//var a = 200;hello(); //var提升作用域
//console.log(3,a)
6、数据类型
序号 |
名称 |
说明 |
1 |
number |
数值型 |
2 |
boolean |
布尔型,true和False |
3 |
String |
字符串 |
4 |
Null |
只有一个null值 |
5 |
Undefined |
变量声明未赋值的 |
6 |
Symbol |
Es6新引入的类型 |
7 |
object类型 |
以上基本类型的复合类型,容器 |
ES是动态弱语言,弱类型语言,虽然先声明了变量,但是变量可以重新赋值任何类型。
//string
console.log('----string-------')
console.log(a = 3+'abc',typeof(a))
console.log(a = null + 'abc',typeof(a))
console.log(a = undefined + 'abc',typeof(a))
console.log(a = true + 'abc',typeof(a))
//number
console.log('----number----')
console.log(a = null + 1,typeof(a))
console.log(a = undefined + 1,typeof(a)) //undefined没有办法转成对应的数字,只是显示男,not a number
console.log(a = true + 8,typeof(a))
console.log(a = false + 8,typeof(a))
//boolean
console.log('----bool----')
console.log(a = null + true,typeof(a))
console.log(a = null + false,typeof(a))
console.log(a = undefined + true,typeof(a)) //undefined没有办法转成对应的数字
console.log(a = undefined + false,typeof(a)) //undefined,不能转成对应的数字
console.log(a = null & true,typeof(a))
console.log(a = undefined & true,typeof(a))
//短路
console.log(a = null && true,typeof(a))
console.log(a = false && null,typeof(a))
console.log(a = false && 'abc',typeof(a))
console.log(a = true && 'abc',typeof(a))
console.log(a = true && '',typeof(a))
//null
console.log(a = null + undefined,typeof(a))
----string-------
3abc string
nullabc string
undefinedabc string
trueabc string
----number----
1 'number'
NaN 'number'
9 'number'
8 'number'
----bool----
1 'number'
0 'number'
NaN 'number'
NaN 'number'
0 'number'
0 'number'
null 'object'
false 'boolean'
false 'boolean'
abc string
string
NaN 'number'
类型运算:
String:与str相加全部转化为str类型。
Number:与number相加全部转化为number
Boolean类型:转为话number类型
弱类型,不需要强制类型转换,会隐士的类型转换。
总结:
遇到字符串,加号就是拼接字符串。
如果没有遇到字符串,加号就是把其他的所有的类型都当做数字处理。
Undefined特殊,因为他没有定义值,所以是一个特殊的数字nan.
如果运算符是逻辑运算符,短路符,返回的就是短路时候的类型,没有隐士转换,
尽量使用显示的转换。
7、字符串
将一个值利用单引号或者双引号引起来就是字符串。
Es6提供了反引号定义一个字符串,可以支持多行,还可以支持插值。
字符串:插值。使用反引号$符号进行插值,赋值即定义。
let a = 'abc'
let b = 'ced'
let c = `line1
line2
line3
`
console.log(c)
let name = 'tom',age = 19
console.log(`hi name is ${name},age${age}`)
line1
line2
line3
hi name is tom,age19
8、转义字符
名称 |
说明 |
\0 |
Null字节 |
\b |
退格符 |
\f |
换页符 |
\n |
换行符 |
\r |
回车符 |
\t |
Tab制表符 |
\v |
垂直制表符 |
\’ |
单引号 |
\” |
双引号 |
\ |
反斜杠符(\) |
\XXX |
由从0到377最多三位八进制数XXX,例如\251是版权符号的八进制序列 |
\xXX |
由从00和FF的两位十六进制数字XX表示的Latin-1字符。\ x A9是版权符号的十六进制序列 |
\uXXXX |
由思维十六进制数字XXXX的Unicode字符,例如,\u 00A9是版权符号的Unicode序列。见Unicode escape sequences(Unicode转义字符) |
\u{XXXXX} |
Unicode代码点(code point)转义字符,例如\u{2F804}相当于Unicode转义字符\uD87E\uDc04的简写。 |
9、字符串操作方法
let a = 'abcdefgh'
console.log(a.charAt(2)) //索引查找字符串
console.log(a[2]) //索引查找字符串
console.log(a.toUpperCase()) // 大写
console.log(a.concat('.com')) //拼接字符串
console.log(a.slice(3)) // 切片
console.log(a.slice(3,6)) //
console.log(a.slice(-2,-1)) //负索引切片
console.log(a.slice(-2)) //负索引切片
c
c
ABCDEFGH
abcdefgh.com
defgh
def
g
gh
let url = 'www.google.com'
console.log(url.split('.')) //以什么进行切割
console.log(url.substr(7,2)) // 返回字符串从何处开始,取多长
console.log(url.substring(7,10)) // 返回子串,从何处开始,到什么为止
[ 'www', 'google', 'com' ]
gl
gle
let url1 = 'www.google.com'
console.log(url1.indexOf('com')) // 查找字符串所在的索引
console.log(url1.replace('.com','.cn')) //替换
console.log(url1.indexOf('gle',3)) // 向右偏移3
url2 = '\tmg edu \r\n'
console.log(url2.trim()) //去除两端的空白字符串
11
www.google.cn
7
mg edu
10、数值型number
在js中,数据均为双精度浮点型范围只能在- +(2^53-1)之间,整型不例外。
数字类型还有三种符号值:+infinity(正无穷) -infinity(负无穷)和nan(not-a-number非数字)
二进制0b。
八进制0o
十六进制0x
指数表示1E3(1000),2e-2(0.02)
常量属性:
数字的方法;
方法 |
描述 |
Number.parseFloat() |
把字符串参数解析成浮点数,和全局方法parseFloat()作用一致 |
Number.parseInt() |
把字符串解析成特定基数对应的整型数字,和全局方法parseInt()作用一致 |
Number.isFinite() |
判断传递的值是否为有限的数字 |
Number.isInteger() |
判断传递的值是否为整数 |
Number.isNaN() |
判断传递的值是否为NaN |
内置数学对象:Math
有绝对值,对数、指数运算、三角函数运算、最大值、最小值、随机数、开方等运算函数。
console.log(Math.PI) //3.14 PI值
console.log(Math.abs(-1)) //绝对值
console.log(Math.log2(16)) // 开方
console.log(Math.sqrt(2)) //平方根
console.log(Math.random()) //随机数
11、运算符
1)算数运算符
+ - * / %
console.log(1/2) //0.5
console.log(1/0) //Infinity
console.log(5%3) //2
console.log(parseInt(1/2)) // 0 向下取整
console.log(parseInt(3/2)) // 1 向下取整
console.log(Math.floor(3/2)) // 1 向下取整
console.log(Math.ceil(3/2)) // 2 向下取整
console.log(Math.round(3/2)) // 2 四舍五入
console.log(Math.round(1/2)) // 1 四舍五入
++和—
单目运算符,表示变量自增,自减
I++ 先用i,用完之后在加1
++I i先自增,在使用i
let i = 0
let a = i++
console.log(a,i) //0 1
console.log(a,i++) // 0 1
a = ++ i
console.log(a,i) // 3 3
单目运算符是优先级高于双目运算符。
7
i = 0;
let a = ++i+i+++i+++i; //++i + i++ i++ +i
console.log(a); // 1 1 2 3 7
2)比较运算符
不做隐士的类型转换写的是===。
不做两个等等号。严格相等使用三个等号。
console.log(100 > '200') // false
console.log(100 > 'a') //false
console.log(300 == '300') //true
console.log(300 === '300') //false
3)逻辑运算符
&&,||,! 与或非
4)位运算
& | ^ ~<< >>位与,位或,异或,取反,左移,右移。
5)三元运算符
条件表达式?真值:假值
console.log(('3' > 30)?'真':'假')
6)逗号操作符
Js运行多个表达式写在一起
let a = 4+5,b = true,c=a > 20?'t':'f'
console.log(a) // 9
console.log(c) // f
7)其他
名称 |
说明 |
Instanceof |
判断是否属于指定类型 |
Typeof |
判断类型字符串 |
Delete |
Delete操作符,删除对象 |
In |
判断指定的属性在对象内,则返回true |
console.log('a' instanceof String) // false
console.log(1 instanceof Number) // false
a = new String('b')
console.log(a instanceof String) // true
console.log(new Number(1) instanceof Number) // true
console.log(a instanceof Object) // true
Instance必须明确使用的类型定义变量。没有 new方法的类型的都不是初始的类型。可以用于继承关系的判断。
Typeof就是返回对象的类型字符串。
Delete删除对象、属性、数组元素
8)运算符优先级
运算符由高到低。
var trees = new Array();
逗号运算符优先级最低,比赋值语句还低。
9)表达式(生产器)
function* inc()
{
let i = 0;
let j = 2;
while(true){
yield i++
if (!j--)return 100;
}
}
let gen = inc()
for (let i = 0 ;i<10;i++)
console.log(gen.next());
{ value: 0, done: false }
{ value: 1, done: false }
{ value: 2, done: false }
{ value: 100, done: true }
{ value: undefined, done: true }
{ value: undefined, done: true }
{ value: undefined, done: true }
{ value: undefined, done: true }
{ value: undefined, done: true }
{ value: undefined, done: true }
每次调用next()方法返回一个对象,这个对象包含两个属性:value和done,value属性表示本次yield表达式的返回值,done属性为布尔值,done是False表示后续还有yield语句执行,如果执行完成或者return后,done为true。
三、js语法
1、语句块
function hello(){
let a = 1;
var b = 2;
c =3
}
if (1)
{
let d = 4;
var e = 5;
f = 6
if (true){
console.log(d)
console.log(e)
console.log(f)
console.log('------')
g = 10
var h = 11
}
}
console.log(e)
console.log(f)
console.log(g)
console.log(h)
4
5
6
------
5
6
10
11
大括号中都是一个作用域。
Let定义的外部不能访问,只能是内部才能访问。Var块作用域,外部可以见到,普通定义外部也可见。
2、条件分支
if (cond1){
}
else if(cond2){
}
else if (cond3){
}
else{
}
条件False等效
False
Undefined
Null
0
NaN
空字符串(””)
3、switch..case分支语句
switch (expression){
case label_1
statements_1
[break;]
case label_2
statements_2
[break;]
default:
statements_def
[break;]
}
let x = 5
switch (x){
case 0:
console.log('zero')
break;
case 1:
console.log('one')
case 2:
console.log('two')
case 3:
console.log('three')
break;
case 4:
console.log('four')
default:
console.log('other')
break;
}
Switch…case语句都可以协程多分支结构。
4、for循环
//for ([initialExpression];[condition];[increamentExpression])
//{
// statement
//}
for (let i=0;i<10;i++){
console.log(i)
}
console.log('---')
5、while循环和do….while循环
While (condition)
Statement
条件满足,进入循环,条件为真,继续循环
do
statement
while (condition);
先进入循环,然后判断,为真就继续循环。
let x = 10;
while(x--){
console.log(x);
}
do{
console.log(x);
}while(x++<10)
打印九九乘法表:
for (let x = 1;x<10;x++)
{
line = '';
for(let y = 1;y<=x;y++)
line += `${x}*${y}=${x*y}`;
console.log(line)
}
5、for…in 循环
对象操作语句for…in用来遍历对象的属性
for (variable in object){
statements}
let arr =[10,20,30,40 ]
console.log(arr[2]) //30
for (let x in arr)
console.log(x)
for (let index in arr)
console.log(`${index}:${arr[index]}`);
for (let i=0;i<arr.length;i++)
console.log(arr[i]);
let obj = {
a:1,
b:'abc',
c:true
};
console.log(obj.a)
console.log(obj['b'])
console.log(obj.d)
console.log('++++++')
for (let x in obj)
console.log(x) //属性名
for (let key in obj) //返回数组的index
console.log(`${key}:${obj[key]}`);
30
0
1
2
3
0:10
1:20
2:30
3:40
10
20
30
40
1
abc
undefined
++++++
a
b
c
a:1
b:abc
c:true
for in 循环返回的是索引或者key,需要简介访问到值。
数组反正返回的是索引,c风格for循环操作简单。
6、for..of循环
Es6的新语法
let arr = [1,2,3,4,5]
let obj = {
a;1,
b:'abc',
c:true
}
for (let i of arr){ //返回数组的元素
console.log(i)
}
for (let i of obj){ //异常,不可以迭代
console.log(i)
}
For….of不能是迭代对象。
原因是of后面必须是一个迭代器(typeerror)
break 、continue
break结束当前循环
continue中断当前煦暖,直接进入下一次循环。
7、for迭代的差别
function sum(arr){
for (let x in arr){
console.log(x,typeof(x),arr[x]);
}
for (let x of arr){
console.log(x,typeof(x));
}
for (let x = 0;x<arr.length;x++){
console.log(x,typeof(x),arr[x])
}
}
X退出的时候还是会加一或减一。
四、函数及作用域
1、函数表达式
function //函数名(参数列表){
//函数体;
return //返回值;
}
function add(x,y){
return x + y
}
console.log(add(3,5))
匿名函数表达式
const add = function(x,y){
return x + y;
};
console.log(add(4,5))
有名字的函数表达式
const add1 = function fn(x,y){
return x + y;
};
console.log(add1(3,4))
有名字的函数表达式,名字只能内部使用,外部不可见
const add2 = function _add(n){
if (n === 1) return n;
return n+ _add(--n)
};
console.log(add2(5))
函数、匿名函数、函数表达式的差异
函数和匿名函数,本质上是一样的,都是函数对象,不过函数都有自己的标识符,函数名,匿名函数需要的是借助其他的标识符而已。
区别在于,函数会声明提升,函数表达式不会。
console.log(add(3,4))
function add(x,y){ //声明提升
return x + y;
};
console.log(sub(4,5)) //会报出异常的,提示sub未定义
const sub = function(x,y){
return x + y;
};
定义函数的形式:function表达式。(有名字和匿名的),有名字的内部使用等。
声明先做,调用后做。
生成器:
const counter = (function *(){
count = 1
while(1)
yield count ++;
})();
console.log(counter.next())
2、高阶函数
高阶函数:函数作为参数或者返回一个函数。
const counter = function(){
let c = 0;
return function(){
return c++
};
};
const c = counter()
console.log(c())
console.log(c())
map函数的实现
const map = function(arr,fn){
newarr = []
for (i in arr){
newarr[i] = fn(arr[i])
}
return newarr
};
console.log(map([1,2,3,4],function(x){
return ++x
}));
3、箭头函数
const map1 = function(arr,fn){
newarr = []
for (i in arr){
newarr[i] = fn(arr[i])
}
return newarr
}
console.log(map([1,2,3,4],x =>++x))
// console.log(map1([1,2,3,4],x =>++x))
// console.log(map1([1,2,3,4],x=>{return ++x}))
console.log(map1([1,2,3,4],(x)=>{return ++x}))
箭头函数就是匿名函数定义。
去掉关键字function。
一个参数的时候可以省掉括号。无参和多参数的时候必须保留括号。多个参数使用逗号分隔。
有大括号的时候必须有return。
定义是定义的形式。调用必须加括号,因为有优先级的问题,所以调用前面的利用括号抱起来。
箭头函数的返回值:
如果函数体部分有多行,就需要使用{},如果有返回值使用关键字return。
如果只有一行语句,可以同时省略大括号和return。
只要有return语句,就不能省略大括号,有return必须有大括号。
只有一条非return语句,加上大括号,函数就是没有返回值了。
Map函数:
function map(arr,fn){
newarr1 = []
for (i in arr)
newarr1[i] = fn(arr[i])
return newarr1
};
console.log(map([1,2,3,4],function(x){
return ++x;
}));
4、参数
传参是按照位置对应的,没有关键字传参这个属性。写的像的话只是赋值语句。
传参的是只是表达式的值。
数组不解构的话当做一个来进行,解构利用…
缺省值可以进行定义的。位置传参的,缺省值往后写
//位置传参
const add = (x,y)=> x+y;
console.log(add(4,5));
//缺省值
const add1 = (x,y=6)=>x+y;
console.log(add1(4))
//非关键字传参,没有关键字传参,只是表达式
const add2 = (x,y)=>x+y;
console.log(add2(z=1,c=2))
//缺省值不能放在前,否则就是转换为3和undefined相加,加过为NaN
const add3 = (x=1,y)=>x+y;
console.log(add3(3)) //NaN
5、可变参数
(1)args
const sum = function(...args){
let resule = 0;
for (i in args){
resule += args[i]
};
return resule
};
console.log(sum(2,3,4))
可变参数使用…args.
(2)arguments对象
const sum = function(...args){
let resule = 0;
console.log(arguments)
for (i in args){
resule += args[i]
};
return resule
};
console.log(sum(2,3,4))
{ '0': 2, '1': 3, '2': 4 }
所有的参数会保存到一个k,v,键值对的字典里面。
(3)参数解构
const add = function (x,y){
return x+y
}
console.log(add(...[100,200,300,400,500])) //参数解构,不需要参数一一对应的。
参数解构,不需要和需要的参数一一对应。
6、返回值
Return的返回值:返回的通常是几个参数,返回的是最后一个参数的值。逗号表达式的。
const add1 = (x,y)=>{return x,y}
console.log(add1(1,2))
表达式的值:
逗号表达式,最后一个的值。
返回 的都只是一个单值。
a = (x = 5,y=6,true);
console.log(a)
b = (x=1,y=4,'abc');
console.log(b)
function c() {
return x= 1,y=2,'abc',false;
}
console.log(c());
7、作用域
Function函数定义,是独立的作用域,内部定义的变量外部不可以见到。
//函数作用域
function c(){
a = 1
var b = 2
let c1 = 3
};
c();
console.log(a)
//console.log(b) // 函数中var定义变量没有方法突破,外界见不到
//console.log(c1) // let定义的外界见不到
//块作用域
if (1){
d = 4
var e = 5
let f = 6
}
console.log(d)
console.log(e) //var 块中定义的才会外界可以见到。
// console.log(f) //let定义外界始终见不到
var b = 2可以提升声明,可以突破非函数的块作用域。
a = 1 隐士声明不能进行提升声明,
let a=3不能提升声明。
严格模式:使用”use strict”语句放到函数的首行,或者js脚本首行。
function show(i,args){
console.log(i,args)
};
x = 100;
function fn(){
let z = 200;
{
var a = 300;
show(1,x)
t = 'free'
let p = 400;
}
var y = 500
show(2,z)
show(3,x)
show(4,a)
show(5,t)
//show(5.5,p) //异常,let出不来上一个语句块
{
show(6,y);
show(7,a)
show(8,t)
{
show(9,a)
show(10,t)
show(11,z)
}
}
}
fn()
// show(12,y) //异常,出不了函数的y
show(13,t)
// show(14,a) //异常,a不能出函数的
show(15,z) //变量声明提升,声明了z,但是还没赋值
var z = 10;
五、js对象模型
基于原型的面向对象语言,而不是基于类的面向对象语言。
基于对象事件。
Js是基于原型的语言,只有原型对象的概念,原型对象就是一个模板,新的对象从这个对象模板构建从而获取最初的属性,任何对象在运行时候可以动态的增加属性。任何一个对象都可以作为另一个对象的原型,后者就可以共享前者的属性。
1、定义类
var obj = {
}
var obj1 = new Object();
var obj2 = new Object;
创建的时候必须使用new方法。
function定义的时候 大驼峰。
字面声明方式。
2、es6之前---构造器
function Point(x,y){
this.x = x
this.y = y
this.show = ()=>console.log(1,this,this.x,this.y)
};
console.log(Point)
p1 = new Point(4,5)
console.log(2,p1)
function Point3D(x,y,z){
Point.call(this,x,y);
this.z = z
console.log(3,'Point 3d')
};
console.log(4,Point3D)
p2 = new Point3D(3,4,5)
console.log(5,p2)
p2.show();
[Function: Point]
2 Point { x: 4, y: 5, show: [Function] }
4 [Function: Point3D]
3 'Point 3d'
5 Point3D { x: 3, y: 4, show: [Function], z: 5 }
1 Point3D { x: 3, y: 4, show: [Function], z: 5 } 3 4
(1)定义一个函数(构造器)对象,函数名首字母大写。
(2)This指代是当前实例的本身。定义属性。
(3)使用new和构造器创建一个通用对象。New操作符会将新的对象的this值传递给point3d构造器函数,函数为这个对象创建z属性。
如果不使用new方法,就是普通的函数调用,this不代表实例。
3、es6中的class
(1)class
Es6开始,提供了关键字class,创建对象更加简单、清晰。
(1)利用关键字class,创建的本质上还是一个函数,是特殊的函数。
(2)一个类只能拥有一个名为constructor的构造器方法,如果没有显示定义一个构造方法,,就会默认添加一个constructor方法。
(3)继承使用extends关键字
(4)一个构造器可以使用super关键字来调用父类的构造函数。
(5)类没有私有属性。
class Point{
constructor(x,y){
this.x = x
this.y = y
}
show(){
console.log(this,this.x,this.y)
}
}
let p1 = new Point(10,11)
p1.show()
class Point3D extends Point{
constructor(x,y,z){
super(x,y);
this.z = z
}
}
let p2 = new Point3D(4,5,6)
p2.show()
Point { x: 10, y: 11 } 10 11
Point3D { x: 4, y: 5, z: 6 } 4 5
重新写show方法;
class Point{
constructor(x,y){
this.x = x
this.y = y
}
show(){
console.log(this,this.x,this.y)
}
}
p1 = new Point(1,2)
console.log(p1)
class Point3D extends Point{
constructor(x,y,z){
super(x,y)
this.z= z
}
show(){
console.log(this ,this.x,this.y,this.z)
}
}
p2 = new Point3D(3,4,5)
console.log(p2)
子类中直接重写父类的方法即可,如果需要使用父类的方法,使用super.method()的方式调用。
使用箭头函数修改:
class Point{
constructor(x,y){
this.x = x
this.y = y
this.show = ()=>console.log('point')
}
}
//继承关系
class Point3D extends Point{
constructor (x,y,z){
super(x,y,z)
this.z = z
this.show=()=>console.log('point3d')
}
}
let p2 = new Point3D(3,4,5);
p2.show()
point3d
子类覆盖,最终显示的结果是point
class Point{
constructor(x,y){
this.x = x
this.y = y
this.show =()=>
console.log('point')
}
}
class Point3D extends Point{
constructor(x,y,z){
super(x,y)
this.z = z
//this.show=()=>console.log('Point3d')
}
show(){
console.log('point3d')
}
}
优先使用实例的方法,show方法,使用this的方法。
class Point{
constructor(x,y){
this.x = x
this.y = y
//this.show =()=>
// console.log('point')
}
show(){
console.log(this,this.x,this.y)
}
}
class Point3D extends Point{
constructor(x,y,z){
super(x,y)
this.z = z
this.show=()=>console.log('Point3d')
}
//show(){
// console.log('point3d')
//}
}
let p1 = new Point3D(2,3,4)
console.log(p1)
p1.show()
属性优先,一定先用属性。优先使用子类的属性。
总结:如果子类和父类使用同一种方式进行定义,子类覆盖父类的。
如果父类使用的属性,子类使用的是方法,那么就是采用父类的属性。
如果子类使用的属性,父类是方法,那么优先使用的是子类的属性方法。。
最终总结,属性优先。。同一类定义的,子类优先
静态属性:
静态方法没有很好的支持的。
(2)静态方法:
在方法名前面加上static,就是静态方法了。
class Add{
constructor(x,y){
this.x = x
this.y = y
}
static show(){
console.log(this.x) //this是Add 不是Add的实例
}
}
a = new Add(2,3)
console.log(a)
//a.show() 实例不能直接访问静态方法
a.constructor.show() //实例可以通过constructor构造器方法访问静态方法。
静态方法总结:实例不能直接访问静态方法,实例必须通过constructor方法访问静态方法。
静态成员必须使用类来定义的。
实例是自己的。
4、this的坑
var shcool = {
name : 'abc',
getNameFunc: function(){
console.log(1,this.name)
console.log(2,this)
return function(){
console.log(3,this === global);
return this.name
}
}
};
console.log(4,shcool.getNameFunc()());
this是全局的global的,所以第三行是true。
第四行,This是global的,所哟下面的return this.name没有name属性。
函数调用的时候调用的方式不用,this的对象就是不同的。
函数执行的时候,会开启新的执行上下文环境executioncontext。
创建this属性。
(1)myfunction(1,2,3)普通的函数调用,this指向的是全局对象,全局对象是nodejs的global或者浏览器的window。
(2)myObject.myFunction(1,2,3),对象的方法调用方式,this指向包含该方法的对象。
(3)call和apply方法的调用,都要看第一个参数是谁。
解决this的问题。
1)显式传入
var shcool1 = {
name : 'cde',
getNameFunc1 : function(){
console.log(this.name)
console.log(this);
return function(that){
console.log(this === global);
return that.name;
}
}
}
console.log(shcool1.getNameFunc1()(shcool1))
cde
{ name: 'cde', getNameFunc1: [Function: getNameFunc1] }
true
cde
利用关键字that 传入对象。主动传入对象,避开了this的问题
2)引入call、apply方法。
var shcool2 = {
name : 'asd',
getNameFunc2:function(){
console.log(this.name)
console.log(this);
return function(){
console.log(this === global);
return this.name
}
}
}
console.log(shcool2.getNameFunc2().call(shcool2));
asd
{ name: 'asd', getNameFunc2: [Function: getNameFunc2] }
false
asd
call方法和apply都是函数对象的方法,第一参数都是传入对象引入的。
Apply传其他参数需要数组。
Call传其他参数需要使用可变参数收集。
3)bind方法
var school3 = {
name: 'asdd',
getNameFunc3:function(){
console.log(1,this.name)
console.log(2,this)
return function(){
console.log(3,this === global);
return this.name;
}
}
};
// console.log(school3.getNameFunc3().bind(school3));
var func = school3.getNameFunc3()
console.log(4,func)
var bindfunc = func.bind(school3)
console.log(5,bindfunc)
console.log(6,bindfunc())
1 'asdd'
2 { name: 'asdd', getNameFunc3: [Function: getNameFunc3] }
4 [Function]
5 [Function: bound ]
3 false
6 'asdd'
Apply、call方法,参数不同,调用时候传入this。
。bind方法是为函数绑定this,调用时候直接调用
4)es6引入的箭头函数定义
var school = {
name :'ass',
getNameFunc:function(){
console.log(this)
console.log(this.name)
return ()=>{
console.log(this === global);
return this.name
}
}
};
console.log(school.getNameFunc()())
{ name: 'ass', getNameFunc: [Function: getNameFunc] }
ass
false
ass
class school{
constructor(){
this.name = 'abcd';
}
getNameFunc(){
console.log(this.name)
console.log(this);
return ()=>{
console.log(this === global);
return this.name
}
}
};
console.log(new school().getNameFunc()())
abcd
school { name: 'abcd' }
false
abcd
绑定之后返回一个新的函数。
全局对象。
全局global,
浏览器中叫做window
解决this语法的利用bind。
5、高阶对象、高阶类、或称为Mixin模式
Mixin,混合模式,不用继承就可以复用的技术,主要还是为了解决多重继承的问题,多继承的路径是个大问题。
Js是基于对象的,类和对象都是对象模板。
混合Mixin,指的四将一个对象的全部或者部分拷贝到另一个对象上去,其实就是属性了。
可以将多个类或对象混合成一个类对象。
指的是将一个对象的全部或者部分拷贝到另一个对象上去。
返回的是定义的类。
class Serialization{
constructor(){
console.log('serialization construtor---');
if (typeof(this.stringify) !== 'function'){
throw new ReferenceError('should define stringify')
}
}
}
class Point extends Serialization{
constructor(x,y){
console.log('Point constructor');
super()
this.x = x
this.y = y
}
stringify(){
return `<point x=${this.x},y=${this.y}>`
}
}
class Point3d extends Point{
constructor(x,y,z){
super(x,y);
this.z = z
}
stringify(){
return `<point x=${this.x},y=${this.y},z=${this.z}>`
}
}
p = new Point(4,5)
console.log(p.stringify())
p3d = new Point3d(7,8,9)
console.log(p3d.stringify())
Point constructor
serialization construtor---
<point x=4,y=5>
Point constructor
serialization construtor---
<point x=7,y=8,z=9>
高阶对象实现,将类的继承构建成为箭头函数
//普通继承
class A extends Object{};
console.log(A)
//匿名类
const A1 = class{
constructor(x){
this.x = x;
}
}
console.log(A1)
console.log(new A1(100).x)
//匿名继承
const B = class extends Object{
constructor(){
super()
console.log('B constorse')
}
};
console.log(B)
b = new B()
console.log(b)
[Function: A]
[Function: A1]
100
[Function: B]
B constorse
B {}
Point3d调用父类serialization,此类然后调用继承point类。
尽量少的改变原有代码的方式增强注入函数的功能。Extends继承,就是Mixin类,混入的类型。
React框架大量使用了Mixin类。
const x = (Sup) =>{
return class extends Sup{
constructor(){
super();
console.log('c constructor')
}
}
};
const c = Sup =>class extends Sup{
constructor(){
super();
console.log('c constorce')
}
};
cls = c(A)
console.log(cls)
a = new cls();
console.log(a);
const Serialization = Sup => class extends Sup{
constructor(...args){
console.log('serialization constructor--')
super(...args);
if (typeof(this.stringify)!=='function'){
throw new ReferenceError('should define stringify')
}
}
}
class Point{
constructor(x,y){
console.log('point constrouct')
this.x =x
this.y =y
}
}
class Point3d extends Serialization(Point){
constructor(x,y,z){
super(x,y)
this.x = x
}
stringify(){
return `<point3d ${this.x}.${this.y}>`
}
}
let p3d = new Point3d(1,2,3)
console.log(p3d.stringify())
serialization constructor--
point constrouct
<point3d 1.2>
Serialization(point)实际上是匿名函数的调用,返回一个新的类型,point3d继承来自这个新的匿名函数类型,增强了功能。
React框架大量使用了Mixin技术。
六、异常
1、抛出异常
使用throw关键字抛出异常。
使用throw关键字可以抛出任意对象的异常
2、捕获
try…catch语句捕获异常
try….catch…finally ,语句捕获异常,finally保证最终一定执行。
try{
throw 1;
}catch (error){
console.log(error.constructor.name);
}finally{
console.log('end')
}
3、模块化
(1)简介
Js主要是在前端的浏览器中使用,js文件下载缓存到客户端,在浏览器中执行。
简单的表单的本地验证,漂浮广告。
服务器端使用ASP、JSP等动态网页技术,将东外生成数据嵌入一个HTML模板中,里面夹杂着js后使用<script>标签,返回给浏览器端。Js只是简单的函数和语句的组合。
2005年后,google大量使用ajax技术,可以一步请求服务器端数据,前端交互的巨大变化,
前端功能需要越来越多,代码多,js文件的增多。全局变量污染,函数名冲突,无法表达脚本之间的依赖关系,用脚本文件先后加载实现的,需要模块化的出现。
2008年v8引擎,2009年nodejs,支持服务器端JS编程,没有模块化是不可以的。
之后产生了commonjs规范,
Common规范,使用全局 require函数导入模块,使用exports导出变量。
AMD(asynchronous module definition)异步模块定义:使用异步方式加载模块,模块的加载不影响他后面语句的执行,所有依赖此模块的语句,都需要定义在一个回调函数里面,回调函数中使用模块的变量和函数,模块加载完成后,回调函数才会执行,就可以安全的使用模块的资源,就是AMD/requires。AMD虽然是异步,但是会预先加载和执行。
CMD(common module definition),使用seajs,作者是淘宝前端玉伯,兼容并包解决了requirejs的问题。Cmd推崇as lazy as possible,尽可能的懒加载。
(2)ES6模块化
Import语句,导入另一个模块导出的绑定。
Export语句,从模块中导入函数、对象、值,供其他模块import导入引用。
(3)导出
建立模块的目录src,此目录下建立mode.js, 内容是导入和导入模块的代买
export default function a(){ //导出缺省的
console.log('a is t1')
};
a();
//导出函数
Export function foo(){
Console.log(‘foo function’);
}
//导出常量
Export const consta = ‘aaa’
(4)导入
import a from './t1-1'
a();
vs code可以很好的语法支持,但是运行环境和v8引擎,不能很好的支持模块化语法。
4、编译器
转译从一种语言代码转换到另一语言代码,当然也可以从高版本转译到低版本的支持语句。
由于js存在不同的版本,不同浏览器兼容问题,使用transpiler转译工具解决。
Babel
开发中比较新的es6的语法,通过转移器指定Wie特定的某些版本代码。
function* inc()
{
let i = 0;
let j = 2;
while(true){
yield i++
if (!j--)return 100;
}
}
let gen = inc()
for (let i = 0 ;i<10;i++)
console.log(gen.next());
转化结果:
"use strict";
var _marked = /*#__PURE__*/regeneratorRuntime.mark(inc);
function inc() {
var i, j;
return regeneratorRuntime.wrap(function inc$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
i = 0;
j = 2;
case 2:
if (!true) {
_context.next = 9;
break;
}
_context.next = 5;
return i++;
case 5:
if (j--) {
_context.next = 7;
break;
}
return _context.abrupt("return", 100);
case 7:
_context.next = 2;
break;
case 9:
case "end":
return _context.stop();
}
}
}, _marked, this);
}
var gen = inc();
for (var i = 0; i < 10; i++) {
console.log(gen.next());
}
缺省必须写在外头,函数和类。
5、预设
有presets,预设的一些
6、转义安装配置****
1)新建文件夹src 和lib
将导入和导出文件放入src文件中。
目录下打开shell 敲入命令npm init 生成package.json文件。
2)设置镜像
.npmrc文件
echo "registry=https://registry.npm.taobao.org" > .npmrc
3)安装
项目根目录下。
$ npm install babel-core babel-cli --save-dev
安装完成后,会在项目根目录下出现node_moudles
4)修改package.json文件
"name": "trans",
"version": "1.0.0",
"description": "trans test",
"main": "index.js",
"directories": {
"lib": "lib"
},
"scripts": {
"build": "babel src -d lib"
},
"author": "",
"license": "ISC",
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-core": "^6.26.3"
}
}
替换为这样的。
"build": "babel src -d lib" 的意思是从src目录转义后输出到lib目录。
7、准备目录
项目根目录下创建src和lib目录。
Src是源码目录。
Lib是目标目录
8、配置babel和安装依赖
touch .babelrc 创建此文件,内容如下 .babelrc没有后缀
{
“presets”:["env"]
}
Env是当前环境自动选择。
安装依赖
npm install babel-preset-env -save-dev
9、准备js文件
export default function a(){
console.log('a is t1.a()')
};
export function b(){
console.log('t1.b()')
};
export let c = 100;
export var d = 200;
export const e = 300;
import a from './mod'
a();
在项目根目录下:
$ npm run build
> trans@1.0.0 build C:\Users\WCL\Documents\trans //
> babel src -d lib //
src\index.js -> lib\index.js //
src\mod.js -> lib\mod.js //
两个文件被转译。
运行文件:
$ node lib/index.js
a is t1.a()
使用babel转译工具转译js非常流行。
可以提高开发效率,兼容性交给转移器处理。
lib 下的index转译后的文件格式:
'use strict';
var _mod = require('./mod');
var _mod2 = _interopRequireDefault(_mod);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
(0, _mod2.default)();
Mod的文件;
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = a;
exports.b = b;
// function a(){
// console.log('test')
// }
// export default
function a() {
console.log('a is t1.a()');
};
function b() {
console.log('t1.b()');
};
var c = exports.c = 100;
var d = exports.d = 200;
var e = exports.e = 300;
10、导入导出
导出文件代码全部在src下的mod.js文件,导入文件都在index.js文件下
export default function a(){
console.log('a is t1')
};
//缺省导出,匿名函数
export default function(){
console.log('default export function')
}
a();
//缺省导入
import defaultFunc from './mod'
defaultFunc();
缺省导入的时候,可以自己重新命名,不需要和缺省导出 的时候一致。
缺省导入,不需要在import后使用花括号。
/**
* 导出举例
*/
//缺省导出类
export default class{
constructor(x){
this.x = x;
}
show(){
console.log(this.x)
}
}
//命名导出函数
export function foo(){
console.log('regular foo()')
}
//函数定义
function bar(){
console.log('regular bar()')
}
//变量常量定义
let x= 100;
var b = 200;
const z=300;
export {bar,x ,y,z};
/**
* 导入
*/
import defaluts,{foo,bar,x,y,z as CONST_c}from './mod'
foo();
bar();
console.log(x);
console.log(y);
console.log(CONST_c);
new defaluts(100).show();
导入所有的导出,会使用一个新的名词空间,使用名词空间可以避免冲突。
Import * as newmod from ‘./mod’;
Newmod.foo();
Newmod.bar();
New newmod.default(200).show();
七、解构
Js的参数解构参考文档; https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Spread_syntax
1、列表解构
var a = [1,2,3,4,5,6]
var b = [...a,7,8,9,0]
console.log(b)
[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 ]
2、参数解构
//参数
function f(x,y,z){
console.log(x+y+z)
}
var args = [1,2,3,4]
f(...args);
结果为6,按照位置一一对应参数,多了的参数不用管,少的参数直接为undefined。
3、数组
解构的时候也是利用数组进行接数值。
//数组解构
const arr = [1,2,3]
let [x,y,z] = arr
console.log(x,y,z)
//丢弃元素
const [,b,] = arr
console.log(b)
//少于数组元素
const[c,d] = arr
console.log(c,d)
//多余数组元素
const[e,f,g,h] = arr
console.log(e,f,g,h)
//可变变量
const[i,...args] = arr
console.log(i)
console.log(args) //可变的参数args收集多个,利用的数数组
//支持默认参数
const[k=20,l=34,,,p=222] = arr
console.log(k,l,p) //没有的话默认使用默认参数,有的话优先使用数组的元素
数组,可变参数的收集就是数组。
可变参数不能给缺省值。
可以丢弃,参数可以多,可以少,少的全部使用undefined。
4、对象解构
const obj = {
a:100,
b:200,
c:300
};
//let {x,y,z} = obj;
//console.log(x,y,z) //undefined undefined undefined 名字要与之对应的。
let {a,b,c} = obj
console.log(a,b,c) //名字是key的名字, 100,200,300
//let {a:x,b:y,c:z} = obj //设置别名,
//console.log(a,b,c)
//console.log(x,y,z) //利用别名打印出消息
//缺省值
let {a:x,b:y,c:z='abc'} = obj
console.log(x,y,z)
结构的时候,提供对象的属性名(key),可以根据属性名找到对应的值, 没有找到对应的值使用缺省值,没有缺省值的话就是undefined,也可以采用别名的方式,把key改成别名,通过别名进行访问属性的值。
别名:
Obj解构。
5、复杂结构
(1)嵌套的数组:
const arr = [1,[2,3],4]
const [a,[b,c],d] = arr
console.log(a,b,c,d) //1 2 3 4
const [e,f] = arr
console.log(e,f) //嵌套的可以接一个数组,作为外层的一个元素。
//1 [ 2, 3 ]
let [g,h,i,j=19] = arr
console.log(g,h,i,j) //1 [ 2, 3 ] 4 19
var [k,...l] = arr
console.log(k,l) //1 [ [ 2, 3 ], 4 ]
(2)对象
var atim = {
title:'secation',
tranliat:[
{
local:'def',
local_tart:[],
last_dict:'2222222',
url:'/def/ac/asd/ffff',
titile:'java'
}
],
url:'wwwww.xxxxxx.com'
}
// let {titile,tranliat,url} = atim
// console.log(titile,tranliat,url)
let{titile,tranliat:[{local}],url} = atim
console.log(local)
还是利用属性名(key)查询所对应的值。
6、数组的操作
方法 |
描述 |
Push(…items) |
尾部增加多个元素 |
Pop() |
移除最后一个元素,并返回 |
Map |
引入处理函数中来处理数组中的每一个元素,返回新的数组 |
Filter |
引入处理函数处理数组中的每一个元素,此处理函数返回true的元素保留,否则该元素被过滤掉了,保留的元素构成新的数组返回。 |
Foreach |
迭代所有元素,无返回值 |
const arr = [1,2,3,4,5,6]
arr.push(6,7,8,9,0)
console.log(arr)
arr.pop()
console.log(arr)
const newarr = arr.map(x=>x*x)
console.log(newarr)
let newarr1 = arr.filter(x=>x%2==0)
console.log(newarr1)
let newarr2 = arr.forEach(x=>x+1)
console.log(newarr2) //没有新的返回值
练习题:
s = Math.sqrt(10)
const arr = [1,2,3,4,5]
//console.log(arr.filter(x=>x%2==0 && x>3).map(x=>x*x))
//console.log(arr.filter(x=>x>s && x%2==0).map(x=>x*x))
let newarr = []
arr.forEach(x=>{
if (x>s && x%2==0) newarr.push(x*x)
})
console.log(newarr)
map返回每次都会返回一个新的值。
filter过滤解决,返回一个新的。
foreach迭代元素,没有任何返回值,返回新的结果是采用新的数组。
数据算数的问题:能过滤先过滤,先过滤在进行计算。
7、对象操作
Object静态方法 |
描述信息 |
Object.keys(obj) |
Es5开始,返回所有的key |
Object.values(obj) |
返回所有值 |
Object.entries(obj) |
返回所有值 |
Object.assign(target,…sources) |
使用多个source对象,来填充target对象,返回target对象 |
const obj = {
a:100,
b:200,
c:300
};
console.log(Object.keys(obj));
console.log(Object.values(obj))
console.log(Object.entries(obj)) //二元数组
var atim = {
title:'secation',
tranliat:[
{
local:'def',
local_tart:[],
last_dict:'2222222',
url:'/def/ac/asd/ffff',
titile:'java'
}
],
url:'wwwww.xxxxxx.com'
}
var copy = Object.assign(
{},atim,{
name:'xxxxxx',url:'www.xxxx.com'
},{tranliat:null}
)
console.log(copy)
{ title: 'secation',
tranliat: null,
url: 'www.xxxx.com',
name: 'xxxxxx' }
属性和值的动态增加和替换等,返回新的。
八、promise
1、概念
Promise对象用于一个异步操作的最终完成(包括成功和失败,)即结果值的表示。
处理异步请求,之所以叫做promise,就是承诺,如果成功怎么处理,失败怎么处理。
语法格式:
new promise(
//executor函数
function(resolve,reject){….});
(1)executor
是一个带有resolve和reject两个参数的函数,
Executor函数在promise构造函数执行同步执行,被传递resolve和reject函数(execu函数在promise构造函数返回前被调用)
Executor内部通常会执行一些异步操作,一旦成功,可以调用resolve函数将promise状态改成fulfilled即完成,或者在发生错误的时候将其状态改为rejected失败
如果在executor函数中抛出一个错误,那么该promise状态为rejected,executo函数的返回值被忽略。
Executor中,resolve或者reject函数只能执行一个。
(2)promise状态
Pending初始状态,不是成功和失败的状态
Fulfilled意味成功
Rejected,意味着失败了。
(3)promise.then(onfulfilled,Onrejected)
参数是两个函数,根据promise的状态来调节不同的函数,fulfilled走的是onfulfiled,reject走的Onrejected。Then得返回值是一个新的promise对象,调用任何一个参数后,其返回值会被新的promise对象来resolve向后传递,
var myPromise = new Promise((resolve,reject)=>{
resolve('ok')
console.log('======')
reject('no') //不会执行到
})
console.log(myPromise);
myPromise.then(
(value)=>console.log(1,myPromise,value),
(reason)=>console.log(2,myPromise,reason)
)
======
Promise { 'ok' }
1 Promise { 'ok' } 'ok'
(4)catch(onrejected)
为当前promise对象添加一个拒绝回调,返回一个新的promise对象,onrejected函数调用其返回值会被新的promise对象用来resolve。
var myPromise = new Promise((resolve,reject)=>{
resolve('ok')
console.log('======')
reject('no')
})
console.log(myPromise);
//链式处理
myPromise.then(
/**
成功就会显示结果
*/
(value)=>console.log(1,myPromise,value),
/**
* 失败就显示原因
*/
(reason)=>console.log(2,myPromise,reason)
).then(
function(v){
console.log(2.5,v);
return Promise.reject(v+'++++++')
}
).catch(reason=>{
console.log(3,reason)
return Promise.resolve(reason)
})
(5)异步调用实例
function runAsync(){
return new Promise(function(resolve,reject){
setTimeout(function(){
console.log('do sth...')
resolve('ok...')
},3000);
})
}
runAsync().then(value=>{
console.log(value)
return Promise.reject(value+'*')
}).catch(reason=>{
console.log(reason)
return Promise.resolve(reason+'*')
}).then(value=>{
console.log(value)
console.log('end')
})
console.log('----fin-----')
----fin-----
do sth...
ok...
ok...*
ok...**
end
不会阻塞执行,按照顺序执行,顺序执行后按照要求进行调度处理。
严格模式:定义常量时候必须使用var这些等。