0%

构造对象

new

new 运算符接受一个函数 F 及其参数: new F(arguments…).这个一过程分为:

  1. 创建类的实例.这步是把一个空的对象的 proto 属性设置为 F.prototype.
  2. 初始化实例.函数 F 被传入参数并调用.关键字 this 被设定为该实例.
  3. 返回实例.

对于实例对象来说,都是通过 new 产生的,无论是function Foo()还是let a = { b : 1 }

1
2
3
4
5
6
7
8
9
function person(name, age) {
this.name = name;
this.age = age;
this.sayName = function () {
console.log(this.name);
};
}

p1 = new person("tom", 12);

instanceof

instanceof 是一个操作符,可以判断对象是否为某个类型的实例

1
2
3
p1 instanceof person; //true,说明p1是由person创造的
p1 instanceof Object; //true,
1 instanceof Number; //false,instanceof判断的是对象

原型与原型链

  1. new一个函数的时候会创建一个对象,函数.prototype等于被创建对象.__proto__
  2. 一切函数都是由 Function 这个函数创建的,所以Function.prototye === 被创建函数.__proto__
  3. 一切函数的原型对象都是由Object这个函数创建的,所以Object.prototype === 一切函数.prototype.__proto__

__proto__把对象和原型连接起来,形成原型链

this

在函数被直接调用时this绑定到全局对象.即 window

1
2
3
4
5
console.log(this); //指向window
function fn1() {
console.log(this); //指向window
}
fn1(); //相当于window.fn1()

内部函数

函数嵌套产生的内部函数的this不是他的父函数,仍然是全局,window

1
2
3
4
5
6
7
8
function fn0() {
function fn() {
console.log(this);
}
fn();
}

fn0();

setTimeOut()和 setInterval()

这两个函数执行的函数 this 也是全局

1
2
3
4
5
6
7
8
9
10
document.addEventListener(
"click",
function (e) {
console.log(this);
setTimeout(function () {
console.log(this);
}, 200);
},
false
);

DOM 对象绑定事件

在事件处理程序中 this 代表事件源 DOM 对象

1
2
3
4
5
6
7
8
9
10
11
12
document.addEventListener(
"click",
function (e) {
console.log(this); //指向dom对象
var _document = this;
setTimeout(function () {
console.log(this); //指向window
console.log(_document);
}, 200);
},
false
);

Function.prototype.bind()

bind 改变 this 的指向.返回一个新函数,并使函数内部的 this 为传入的第一个参数,并不执行

1
2
var fn3 = obj1.fn.bind(obj1);
fn3();

使用 call 和 apply 设置 this

call,apply 调用一个函数,传入函数执行上下文及其参数,并立即执行

1
2
fn.call(context,param1,param2...)//接受参数列表
fn.apply(context,paramArray)//接收参数数组

第一个参数都是希望设置的 this 对象

举例

1
2
3
4
5
6
7
8
//实现一个函数可以遍历传入的参数
function sum() {
//arguments.forEach无法执行,因为arguments不是数组,使用call将forEach的this指向arguments,使之可以遍历
Array.prototype.forEach.call(arguments, function (value) {
console.log(value);
});
}
sum(3, 4, 1, 6); //函数就可以遍历了
1
2
3
4
5
6
7
8
9
10
function sum() {
var result = 0;
//arguments.forEach无法执行,因为arguments不是数组,使用call将forEach的this指向arguments,使之可以遍历
Array.prototype.forEach.call(arguments, function (value) {
console.log(value);
return (result += value);
});
console.log(result);
}
sum(3, 4, 1, 6); //函数就可以将传入值相加了

arguments

  1. 在函数调用时,会自动在该函数内部生成一个名为 arguments 的隐藏对象
  2. 该对象类似于数组,可以使用[]运算符获取函数调用时传递的实参
  3. 只有函数被调用时,arguments 对象才会创建,未调用时其值为 null
1
2
3
4
5
6
7
8
function fn5(name, age) {
console.log(arguments);
name = "XXX";
console.log(arguments);
arguments[1] = 30;
console.log(arguments);
}
fn5("Byron", 20);

研究 this

1
2
3
4
5
6
7
8
9
var obj = {
foo: function () {
console.log(this);
},
};

var bar = obj.foo;
obj.foo(); // 打印出的 this 是 obj
bar(); // 打印出的 this 是 window

实际上的正常调用方式

func.call(context, p1, p2)

其他简化方式都可以转化

1
2
3
4
5
func(p1, p2) 等价于
func.call(undefined, p1, p2)

obj.child.method(p1, p2) 等价于
obj.child.method.call(obj.child, p1, p2)

this,就是上面代码中的 context。就这么简单。

this 是你 call 一个函数时传的 context,由于你从来不用 call 形式的函数调用,所以你一直不知道。

浏览器规则:如果你传的 context 就 null 或者 undefined,那么 window 对象就是默认的 context(严格模式下默认 context 是 undefined)

上面的代码就解释的通了

1
2
3
4
5
6
7
8
9
10
11
12
13
var obj = {
foo: function () {
console.log(this);
},
};

var bar = obj.foo;
obj.foo(); // 转换为 obj.foo.call(obj),this 就是 obj
bar();
// 转换为 bar.call()
// 由于没有传 context
// 所以 this 就是 undefined
// 最后浏览器给你一个默认的 this —— window 对象

Event Handler 中的 this

1
2
3
btn.addEventListener("click", function handler() {
console.log(this); // 请问这里的 this 是什么
});

当使用 addEventListener() 为一个元素注册事件的时候,句柄里的 this 值是该元素的引用。其与传递给句柄的 event 参数的 currentTarget 属性的值一样。

jQuery Event Handler 中的 this

那么下面代码中的 this 是什么呢:

1
2
3
$ul.on("click", "li", function () {
console.log(this);
});

当 jQuery 的调用处理程序时,this 关键字指向的是当前正在执行事件的元素。对于直接事件而言,this 代表绑定事件的元素。对于代理事件而言,this 则代表了与 selector 相匹配的元素。(注意,如果事件是从后代元素冒泡上来的话,那么 this 就有可能不等于 event.target。)若要使用 jQuery 的相关方法,可以根据当前元素创建一个 jQuery 对象,即使用 $(this)。

[]语法

1
2
3
4
5
function fn() {
console.log(this);
}
var arr = [fn, fn2];
arr[0](); // 这里面的 this 又是什么呢?

我们可以把 arr0 想象为 arr.0( ),虽然后者的语法错了,但是形式与转换代码里的 obj.child.method(p1, p2) 对应上了,于是就可以愉快的转换了:

1
2
3
4
         arr[0]()
假想为 arr.0()
然后转换为 arr.0.call(arr)
那么里面的 this 就是 arr 了

call、apply 、函数执行的本质

当我们执行一个函数,以下几种调用方式等价

1
2
3
4
5
6
7
8
"use strict";
function fn(a, b) {
console.log(this);
}
fn(1, 2);
//等价于
fn.call(undefined, 1, 2);
fn.apply(undefined, [1, 2]);

在严格模式下, fn 里的 this 就是 call 的第一个参数,也就是 undefined。

在非严格模式下(不加”use strict”), call 传递的第一个参数如果是 undefined 或者 null, 那 this 会自动替换为 Window 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var obj = {
fn: function (a, b) {
console.log(this);
},
child: {
fn2: function () {
console.log(this);
},
},
};
obj.fn(1, 2);
//等价于
obj.fn.call(obj, 1, 2); // 所以 this 是 obj
obj.fn.apply(obj, [1, 2]);

obj.child.fn2();
//等价于
obj.child.fn2.call(obj.chid); // 所以 this 是 obj.child

箭头函数中的 this

  1. 箭头函数会捕获其所在上下文的 this 值作为自己的 this 值,自己本身并没有 this 值.
  2. 箭头函数的 this 永远指向其上下文的 this,任何方法都改变不了其指向,如 call,bind,apply.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
let app = {
fn1: function (a) {
console.log(this); //app
},
fn2(a) {
console.log(this); //app
},
fn3: (a) => {
console.log(this); //window
},
};

app.fn2.call(app);
app.fn3.call(它上下文的this);

箭头函数的复杂情况示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
var app = {
fn1() {
setTimeout(function () {
console.log(this);
}, 10);
},
fn2() {
setTimeout(() => {
console.log(this);
}, 20);
},
fn3() {
setTimeout(
function () {
console.log(this);
}.bind(this),
30
);
},
fn4: () => {
setTimeout(() => {
console.log(this);
}, 40);
},
};

app.fn1();
app.fn2();
app.fn3();
app.fn4();

以上代码相当于;
var app = {
fn1() {
function fn() {
console.log(this);
}
//过10ms后执行
//fn.call(undefined),所以输出window
},
fn2() {
//过20ms执行箭头函数
//箭头函数里没有资格有自己的this,借用setTimeout外面的this,也就是app
},
fn3() {
//创建了一个新函数,这个新函数绑定了外面的this,也就是app
//20ms后执行新函数,输出this,也就是刚刚绑定的app
},
fn4: () => {
//过40ms执行箭头函数
//箭头函数里没有资格有自己的this,借用setTimeout外面的this
//setTimeout所在的fn4也是箭头函数,没资格拥有自己的this,借用外面的this,也就是window
},
};

函数的执行环境

JavaScript 中的函数既可以被当作普通函数执行,也可以作为对象的方法执行,这是导致 this 含义如此丰富的主要原因

一个函数被执行时,会创建一个执行环境(ExecutionContext),函数的所有的行为均发生在此执行环境中,构建该执行环境时,JavaScript 首先会创建 arguments 变量,其中包含调用函数时传入的参数

接下来创建作用域链,然后初始化变量。首先初始化函数的形参表,值为 arguments 变量中对应的值,如果 arguments 变量中没有对应值,则该形参初始化为 undefined。

如果该函数中含有内部函数,则初始化这些内部函数。如果没有,继续初始化该函数内定义的局部变量,需要注意的是此时这些变量初始化为 undefined,其赋值操作在执行环境(ExecutionContext)创建成功后,函数执行时才会执行,这点对于我们理解 JavaScript 中的变量作用域非常重要,最后为 this 变量赋值,会根据函数调用方式的不同,赋给 this 全局对象,当前对象等

至此函数的执行环境(ExecutionContext)创建成功,函数开始逐行执行,所需变量均从之前构建好的执行环境(ExecutionContext)中读取.

每个执行上下文中都有三个重要的属性

  • 变量对象(VO),包含变量、函数声明和函数的形参,该属性只能在全局上下文中访问
  • 作用域链(JS 采用词法作用域,也就是说变量的作用域是在定义时就决定了)
  • this

三种变量

实例变量:(this)类的实例才能访问到的变量

静态变量:(属性)直接类型对象能访问到的变量

私有变量:(局部变量)当前作用域内有效的变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function ClassA() {
var a = 1; //私有变量,只有函数内部可以访问
this.b = 2; //实例变量,只有实例可以访问
}

ClassA.c = 3; // 静态变量,也就是属性,类型访问

console.log(a); // error
console.log(ClassA.b); // undefined,这里的this指向window
console.log(ClassA.c); //3

var classa = new ClassA();
console.log(classa.a); //undefined
console.log(classa.b); // 2,这里的this指向实例
console.log(classa.c); //undefined

继承

继承是指一个对象直接使用另一个对象的属性和方法.

如果实现以下两点就实现了继承

  1. 得到一个类的属性
  2. 得到一个类的方法

原型继承,核心在于在子类的构造函数中通过parent.call(this)继承父亲的属性,然后改变子类的原型为new parent()来继承父类的函数.

属性的获取

对象属性的获取是通过构造函数的执行.在一个类中执行另外一个类的构造函数,就可以把属性赋值到自己内部,

但是需要把环境改到自己的作用域内,用call修改 this 的指向即可.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function Person(name, sex) {
this.name = name;
this.sex = sex;
}

Person.prototype.printName = function () {
console.log(this.name);
};

function Male(age) {
this.age = age;
}

Male.prototype.printAge = function () {
console.log(this.age);
};

function Male(name, sex, age) {
Person.call(this, name, sex);
this.age = age;
}
Male.prototype.printAge = function () {
console.log(this.age);
};

var m = new Male("Tom", "male", 10);
console.log(m.sex); //'male'

继承的范例

1
2
3
4
5
6
7
8
9
10
function Male(name, sex, age) {
Person.call(this, name, sex);
this.age = age;
}

Male.prototype = Object.create(Person.prototype);

Male.prototype.printAge = function () {
console.log(this.age);
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function Person(name, sex) {
this.name = name;
this.age = age;
}

Person.prototype.printName = function () {
console.log(this.name);
};

function Male(name, sex, age) {
Person.call(this, name, age);
this.sex = sex;
}

Male.prototype = new Person();

Male.prototype.constuctor = Male;

Male.prototype.printAge = function () {
console.log(this.age);
};

var man = new Male("Tom", "Male", 10);
man.printName(); //'Tom'

hasOwnProperty

判断属性是自己的还是继承的

1
2
m.hasOwnProperty("name"); //true
m.hasOwnProperty("printName"); //false

Promise 对象

回调地狱

下列代码实现按顺序执行,1 秒后执行 fn1,再过 1 秒执行 fn2,再过 1 秒执行 fn3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function fn1(callback) {
setTimeout(() => {
console.log("fn1");
callback();
}, 1000);
}

function fn2(callback) {
setTimeout(() => {
console.log("fn2");
callback();
}, 1000);
}

function fn3() {
setTimeout(() => {
console.log("fn3");
}, 1000);
}

fn1(function () {
fn2(function () {
fn3();
});
});

由于层层嵌套,形成回调地狱.(如果套个十几二十个,真的要崩溃)

什么是 Promise

Promise 对象用于表示一个异步操作的最终完成 (或失败), 及其结果值.(MDN 解释)

Promise 是一个对象,对象里存储着状态.分别是 pending(等待态),fulfilled(完成态),rejected(拒绝态)

Promise 启动之后,当满足成功的条件时我们让状态从 pending 变成 fullfilled (执行 resolve);当满足失败的条件,我们让状态从 pending 变成 rejected(执行 reject)

面试官想听的版本:

所谓 Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理,让开发者不用再关注于时序和底层的结果。Promise 的状态具有不受外界影响和不可逆两个特点。

then 函数会返回一个 Promise 实例,并且该返回值是一个新的实例而不是之前的实例。因为 Promise 规范规定除了 pending 状态,其他状态是不可以改变的,如果返回的是一个相同实例的话,多个 then 调用就失去意义了.

then 的参数有两个,也是一个成功函数,一个失败函数.

1
const promise2 = doSomething().then(successCallback, failureCallback);

Promise+ajax 范例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//获取IP
function getIp(){
return new Promise(function(resolve,reject){
var xhr = XMLHttpRequest()
xhr.open('GET','https://easy-mock.com/mock/5ac2f80c3d211137b3f2843a/promise/getIp',true)
xhr.onload = function(){
var retJson = JSON.parse(xhr.responText)
resolve(retJson.ip)
}
xhr.onerror = function(){
reject('获取IP失败')
}
xhr.send()
})
}

//从IP获取城市
function getCityFromIp(ip){
return new Promise(function(resolve, reject){
var xhr = XMLHttpRequest()
xhr.open('GET', 'https://easy-mock.com/mock/5ac2f80c3d211137b3f2843a/promise/getCityFromIp?ip='+ip, true)
xhr.onload = function(){
var retJson = JSON.parse(xhr.responText)
resolve(retJson.city)
}
xhr.onerror = function(){
reject('获取city失败')
}
xhr.send()
})
}

//通过城市获取天气
function getWeatherFromCity(city{
return new Promise(function(resolve, reject){
var xhr = XMLHttpRequest()
xhr.open('GET', 'https://easy-mock.com/mock/5ac2f80c3d211137b3f2843a/promise/getWeatherFromCity?city='+city, true)
xhr.onload = function(){
var retJson = JSON.parse(xhr.responText)
resolve(retJson)
}
xhr.onerror = function(){
reject('获取天气失败')
}
xhr.send()
})
})

getIp().then(function(ip){
return getCityFromIp(ip)
}).then(function(city){
return getWeatherFromCity(city)
}).then(function(data){
console.log(data)
}).catch(function(e){
console.log('出现了错误',e)
})
//getIP()得到一个promise对象,传入ip,返回另一个promise对象,依次往下
//中途报错直接执行catch()

Promise.all

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function getCityFromIp(ip){
return new Promise(function(resolve, reject){
var xhr = XMLHttpRequest()
xhr.open('GET','https://easy-mock.com/mock/5ac2f80c3d211137b3f2843a/promise/getCityFromIp?ip='+ip',true)
xhr.onload = function(){
var retJson = JSON.parse(xhr.responText)
resolve(retJson)
}
xhr.onerror = function(){
reject('获取city失败')
}
xhr.send()
})
}

var p1 = getCityFromIp('10.10.10.1')
var p2 = getCityFromIp('10.10.10.2')
var p3 = getCityFromIp('10.10.10.3')

//Promise.all, 当所有的 Promise 对象都完成后再执行
Promise.all([p1,p2,p3]).then(data => {
console.log(data)
})

Promise.race

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function getCityFromIp(ip) {
var promise = new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open(
"GET",
"https://easy-mock.com/mock/5ac2f80c3d211137b3f2843a/promise/getCityFromIp?ip=" +
ip,
true
);
xhr.onload = function () {
var retJson = JSON.parse(xhr.responseText); // {"city": "hangzhou","ip": "23.45.12.34"}
resolve(retJson);
};
xhr.onerror = function () {
reject("获取city失败");
};
setTimeout(() => {
xhr.send();
}, Math.random() * 1000);
});
return promise;
}

var p1 = getCityFromIp("10.10.10.1");
var p2 = getCityFromIp("10.10.10.2");
var p3 = getCityFromIp("10.10.10.3");

//Promise.race, 谁的先输出就先执行谁
Promise.race([p1, p2, p3]).then((data) => {
console.log(data);
});

callback&Promise&async/await

把一个需求不断简化

需求如下:

  1. 读取 a.md 文件,得到内容
  2. 把内容转换成 HTML 字符串
  3. 把 HTML 字符串写入 b.html

callback()处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var fs = require("fs");
var markdown = require("markdown").markdown;
fs.readFile("a.md", "UTF-8", function (err, str) {
if (err) {
return console.log(err);
}
var html = markown.toHTML(str);
fs.writeFile("b.html", html, function (err) {
if (err) {
return console.log(err);
}
console.log("write.success");
});
});

ES6 语法简化处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let fs = require("fs");
let markdown = require("markdown").markdown;
fs.readFile("a.md", "UTF-8", (err, str) => {
if (err) {
return console.log(err);
}
let html = markdown.toHTML(str);
fs.writeFile("b.html", html, (err) => {
if (err) {
return console.log(err);
}
console.log("write.success");
});
});

Promise 处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
const fs = require("fs");
const markdown = require("markdown").markdown;

readFile("a.md")
.then((mdStr) => {
return markdown.toHTML(mdStr); //返回结果作为下个回调函数
})
.then((html) => {
writeFile("b.html", html);
})
.catch((e) => {
console.log(e);
});

//对读取文件进行包装
function readFile(url) {
return new Promise((resolve, reject) => {
fs.readFlie(url, "UTF-8", (err, str) => {
if (err) {
reject(new Error("readFlie error"));
} else {
resolve(str);
}
});
});
}

//对写入文件进行包装
function writeFile(url, data) {
return new Promise((resolve, reject) => {
fs.writeFile(url, data, (err, str) => {
if (err) {
reject(new Error("write error"));
} else {
resolve();
}
});
});
}

使用模块改装上面代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const markdown = require("markdown").markdown;
const fsp = require("fs-promise"); //用于把fs变promise化
let onerror = (err) => {
console.error("something wrong");
};

fsp
.readFile("a.md", "UTF-8")
.then((mdStr) => {
return markdown.toHTML(mdStr);
})
.then((html) => {
fsp.writeFile("b.html", html);
})
.catch(onerror);

async/await 处理

1
2
3
4
5
6
7
8
9
10
11
12
13
const markdown = require("markdown").markdown;
const fsp = require("fs-promise");
let onerror = (err) => {
console.error("sonething wrong");
};

async function start() {
let mdStr = await fsp.readFile("a.md", "UTF-8");
let html = markdown.toHTML(mdStr);
await fsp.writeFile("b.html", html);
}

start().catch(onerror);

方法

Promise.prototype.then(onFulfilled, onRejected)

接收成功或失败的结果回调到当前 promise, 返回一个新的 promise, 将以回调的返回值来 resolve.

Promise.prototype.catch(onRejected)

接受一个失败的结果回调到当前 promise, 返回一个新的 promise。当这个回调函数被调用,新 promise 将以它的返回值来 resolve,否则如果当前 promise 进入 fulfilled 状态,则以当前 promise 的完成结果作为新 promise 的完成结果.

Promise.prototype.finally(onFinally)

添加一个事件处理回调于当前 promise 对象,并且在原 promise 对象解析完毕后,返回一个新的 promise 对象。回调会在当前 promise 运行完毕后被调用,无论当前 promise 的状态是完成(fulfilled)还是失败(rejected)

async/await

async 函数的创建时通过在函数声明语句之前加上 async 关键字,

这是异步函数的特征之一 ––它将任何函数转换为 promise。

示例如下:

1
2
3
4
5
6
7
const asyncFunction = async () => {
// Code
};
//或者
async function asyncFunction() {
//code
}

await

async 异步函数可通过await来暂停,该关键字只能用在 async 函数内部。

每当函数执行完毕,await返回的是任何 async 函数会返回的东西。

阮一峰老师的解释: async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句

只能放在 async 函数内部使用,不能放在普通函数里面,否则会报错。

后面放 Promise 对象,在Pending状态时,相应的协程会交出控制权,进入等待状态。等等党永不为奴!这个是本质。

await是 async wait 的意思,wait 的是resolve(data)消息,并把数据data返回。

await后面也可以跟同步代码,不过系统会自动转化成一个 Promise 对象。

await只关心异步过程成功的消息resolve(data),拿到相应的数据data。至于失败消息reject(error),不关心,不处理。

await 的特点

1.建立在 promise 之上。所以,不能把它和回调函数搭配使用。但它会声明一个异步函数,并隐式地返回一个 Promise。因此可以直接 return 变量,无需使用 Promise.resolve 进行转换。

2.和 promise 一样,是非阻塞的。但不用写 then 及其回调函数,这减少代码行数,也避免了代码嵌套。而且,所有异步调用,可以写在同一个代码块中,无需定义多余的中间变量。

3.它的最大价值在于,可以使异步代码,在形式上,更接近于同步代码。

4.它总是与 await 一起使用的。并且,await 只能在 async 函数体内。

5.await 是个运算符,用于组成表达式,它会阻塞后面的代码。如果等到的是 Promise 对象,则得到其 resolve 值。否则,会得到一个表达式的运算结果。

MDN 例子:

1
2
3
4
5
6
async function hello() {
return (greeting = await Promise.resolve("Hello"));
}

hello().then(alert);
//先等返回了hello再打印

简单案例:

1
2
3
4
5
6
7
8
9
10
11
12
function timeout(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}

async function asyncPrint(value, ms) {
await timeout(ms);
console.log(value);
}
asyncPrint("hello world", 50);
// 上面代码指定50毫秒以后,输出"hello world"。

单栏布局

方式: 定宽+水平居中

1
2
3
4
5
6
<style>
.layout{
max-width: 100px;
margin: 0 auto;
}
</style>

通栏的话再加一层 div,单独给 div 设置背景色即可

双列布局

固定 margin+浮动元素

先设置浮动,再设置普通元素,最后清除浮动

flex 布局(弹性盒子)

display:flex

一个容器 设置了display:flex属性,就定义了一个 flex 容器.它的直接子元素会接受这个 flex 环境.

常规布局是基于块和内联流方向,而 Flex 布局是基于flex-flow流可以很方便的用来做居中,能对不同屏幕大小自适应。

在布局上有了比以前更加灵活的空间。

这个是 ms 图床

这个是路过图床

flex-direction

1
2
3
.container{
flex-direction: row | row-reverse | column | column-reverse;
}

设置子元素在父容器中的位置

  1. row 默认值,水平从左到右
  2. row-reverse,从右到左
  3. column,垂直从上到下
  4. column-reverse,垂直从下到上

flex-wrap

设置换行

1
2
3
4
.container{
flex-wrap: nowrap | wrap | wrap-reverse;
}
依次为:不换行,换行,换行且颠倒顺序

flex-flow

flex-directionflex-wrap的缩写,默认值row nowrap

flex-flow: <'flex-direction'> || <'flex-wrap'>

justify-content

设置子元素在水平方向上的对齐方式

1
2
3
4
.container{
justify-content: flex-start | flex-end | center | space-between | space-around;
}
分别是水平靠左,水平靠右,水平居中,均匀分布,均匀分布且两端保留子元素间距一半(空间包裹)

align-items

设置子元素在垂直方向上的对齐方式

1
2
3
4
.container{
align-items: flex-start | flex-end | center | baseline | stretch;
}
分别是垂直靠上,垂直靠下,垂直居中,垂直基线对齐,垂直方向拉伸

align-content

设置子元素整体内容的在垂直方向上的对齐方式

1
2
3
4
.container{
align-content: flex-start | flex-end | center | space-between | space-around | stretch;
}
分别是整体靠上,整体靠下,整体居中,整体垂直均匀排列,整体均匀分布且垂直两端保留间距一半空间,整体垂直拉伸占用剩余空间

用在子元素上的属性

order

默认情况下 flex order 会按照书写顺序排列,可以通过 order 属性改变,数值小的在前面,也可以是负数.

1
2
3
.item {
order: <integer>;
}

flex-grow

.item {flex-grow: 1}

按比例瓜分父元素剩余的空间,1 即分一份.不写默认是 0

flex-basis

基准宽度

flex-shrink

按比例吸收超出的空间.不写默认是 1

flex

flex-grow,flex-shrink,flex-basis的缩写

1
2
3
.item{
flex: none | [ <'flex-grow'> <'flex-shrink'> || <''flex-basis'>]
}

aglin-self

单独修改自身的属性

Grid 布局

父元素 Grid container 的属性

display

将元素定义为 gird container,并为其建立新的网格格式化上下文

1
2
3
4
.container{
display: grid | inline-grid | subgrid;
}
分别是生成一个块级网格;一行网格;如果本身是gird-item,可从父元素获取行列大小

gird-template-columns 和 gird-template-rows

设置网格的列和行

1
2
3
4
.container{
gird-template-columns: 40px 50px auto 50px 40px;
grid-template-rows: 25% 100px auto;
}

简化写法:

1
2
3
4
5
6
7
.container{
gird-template-columns: repeat(3, 20px [col-start]) 5%;
}
等价于
.container{
gird-template-columns: 20px [col-start] 20px [col-start] 20px [col-start] 5%;
}

“fr”单位允许将轨道大小设置为网格容器自由空间的一部分.

如下代码会将每个 gird item 设置为 gird container 宽度的三分之一

1
2
3
.container{
.grid-template-columns: 1fr 1fr 1fr;
}

gird-template-areas

  1. — 使用 gird-area 属性设置的网络区域的名称
  2. .  — 点号代表一个空网格单元
  3. none — 没有定义网格单元
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
.item-a {
grid-area: header;
}
.item-b {
grid-area: main;
}
.item-c {
grid-area: sidebar;
}
.item-d {
grid-area: footer;
}
.container {
grid-template-columns: 50px 50px 50px 50px;
grid-template-rows: auto;
grid-template-areas:
"header header header header"
"main main . sidebar"
"footer footer footer footer";
}

这将创建一个四列宽三行高的网格。 整个第一行将由 header 区域组成。 中间一行将由两个 main 区域、一个空单元格和一个 sidebar 区域组成。 最后一行是 footer 区域组成。

媒体查询(响应式)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<style>
/* 屏幕宽度为300px-325px时的css样式 */
@media(min-width: 300px) and (max-width: 325px){
body{
background: red;
}
}

/* 屏幕宽度小于450px时的css样式 */
@media(max-width: 450px){
body{
background: black;
}
}
/* 这样前面那个就没用了,被覆盖了 */
</style>

一般不用第一个方法,直接引用一个手机版的 css 即可

<link rel="stylesheet" media="(max-width:768px)" href="mobile.css">

在屏幕宽度小于 768px 时,就会渲染这个 css,要把这个引用写在 main.css 之后,把 main.css 覆盖。

并加上 meta:vp

<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">

让视口等于理想视口,禁止用户缩放.

介绍

超文本标记语言(Hypertext Markup Language, HTML)是一个可以用来结构化 Web 内容并给予其含义和目标的编码语言。

HTML 并不是真正的的程序语言,它是一种标记语言.

它由一系列的元素(elements)所组成,不同的元素各有功用.

HTML5 是定义 HTML 标准的最新的版本。是一个新版本的 HTML 语言,具有新的元素,属性和行为.

doctype

doctype 是文档类型说明.

<!DOCTYPE html>有什么作用?

告诉浏览器的语言解析器用什么类型解析文档

HTML5 不基于 SGML,因此不需要对 DTD 进行引用,但是需要 doctype 来规范浏览器的行为(让浏览器按照它们应该的方式来运行)

设定文档语言

<html lang="zh-CN">

  • SEO(搜索引擎优化)
  • 有利于视障人士通过读屏器阅读

HTML 头部

元数据<meta>:

指定文档编码: <meta charset="UTF-8">

适配移动页面: <meta name="viewport" content="width=device-width, initial-scale=1.0">

设置 referer: <meta name="referrer" content="never">

添加页面描述: <meta name="description" content="腾讯网(www.QQ.com)是中国浏览量最大...>

定制页面图标: <link rel="shortcut icon" href="favicon.ico" type="image/x-icon">

应用 CSS 和 JS

1
2
<link rel="stylesheet" href="app.css">
<script src="app.js"></script>
  1. link 属于 XHTML 标签,除了加载 CSS 外,还能用于定义 RSS, 定义 rel 连接属性等作用;而@import 是 CSS 提供的,只能用于加载 CSS;
  2. 页面被加载的时,link 会同时被加载,而@import 引用的 CSS 会等到页面被加载完再加载;
  3. import 是 CSS2.1 提出的,只在 IE5 以上才能被识别,而 link 是 XHTML 标签,无兼容问题;
  4. link 支持使用 js 控制 DOM 去改变样式,而@import 不支持;

src 与 href 的区别

区别:src 用于替代这个元素,而 href 用于建立这个标签与外部资源之间的关系

<link href="style.css" rel="stylesheet" />浏览器加载到这里的时候,html 的渲染和解析不会暂停,css 文件的加载是同时进行的

<script src="script.js"></script>当浏览器解析到这句代码时,页面的加载和解析都会暂停直到浏览器拿到并执行完这个 js 文件

标签

行内元素有:a b span img input select strong(强调的语气)

块级元素有:div ul ol li dl dt dd h1 h2 h3 h4…p

空(void)标签就是不用加闭标签的标签,也可以叫闭元素。如:

  • <area>
  • <base>
  • <br>
  • <col>
  • <colgroup> when the span is present
  • <command>
  • <embed>
  • <hr>
  • <img>
  • <input>
  • <keygen>
  • <link>
  • <meta>
  • <param>
  • <source>
  • <track>
  • <wbr>

可替换标签

宽高由自己决定的元素,如:

  • <img>
  • <video>
  • <input>
  • <button>

总结:

常见块级

div:文档节

section:文档节

nav:导航

header:页眉

article:文章

aside:文章侧栏

footer:页脚

details:元素的细节

summary:details 元素可见的标题

dialog:对话框窗口

h1,h2,h3,h4,h5,h6:标题

p:段落

ul:无序列表

ol:有序列表

dir:目录列表

li:项目

dl:列表

dt:列表项目

dd:项目描述

menu:命令的菜单,列表

menuitem:菜单项目

command:命令按钮

form:表单

fieldset:围绕元素的边框(可用于表单内元素分组)

legend:在边框上的标题

select:选择列表(内联元素)

optgroup:组合选择列表选项

option:选择列表选项(也可做 datalist 选项)

datalist:下拉列表(id 要与 input 中 list 属性值绑定)

table:表格

caption:表格标题

thead:组合表头内容(th)

tbody:组合主体内容(td)

tfoot:组合表注内容(td)

tr:表格行

th:表头单元格

td:表格单元

col:表格列属性;(空标签)

colgroup:表格格式化列组;

iframe:内联框架

figure:媒介内容分组

figcaption:figure 标题

map:图像映射

area:图像区域

canvas:图形容器(脚本来绘制图形)

video:视频

source:媒介源

track:文本轨道

audio:声音内容

br:换行(空标签)

hr:水平分割线(空标签)

pre:格式文本

blockquote:块引用

address:文档联系信息

center:居中文本(不赞成使用)

spacer:在水平和垂直方向插入空间(空元素)

常见行内标签

span:内联容器

abbr:缩写

em:强调

strong:粗体强调

mark:突出显示的文本

b:粗体

i:斜体

bdi:文本方向

bdo:文字方向

big:大字体

small:小字体

sup:上标

sub:下标

del:被删除的文本

strike:删除线

s:删除线

ins:被插入的文本

u:下划线

nobr:禁止换行

wbr:单词换行时机(空标签)

tt:打字机文本

kbd:键盘文本

time:日期/时间

cite:引用

q:短引用(“”)

font:字体设定(不常用)

acronym:缩写(html5 不支持)

dfn:字段(不常用)

a:锚点

img:图片

embed:内嵌(空标签)

label:input 标记(点击文本触发控件)

input:输入框

button:按钮

keygen:生成秘钥(空标签)

textarea:多行文本输入框

output:输出结果

ruby:中文注音

rt:注音

rp:浏览器不支持 ruby 元素显示的内容

progress:进度条

meter:度量

var:定义变量

code:计算机代码文本

samp:计算机代码样本

select:下拉列表

文本

HTML 包括六个级别的标题, <h1> – <h6>

通常一个页面只有一个<h1>,请尽量按顺序使用 <h1> – <h6>

段落:<p></p>

无序列表: <ul><li></li></ul>

有序列表: <ol><li></li></ol>

描述列表:

1
2
3
4
<dl>
<dt>吃藕</dt>
<dd>chi ou = 吃藕 = 丑 例句:被老板忽悠剪了个吃藕的发型。<dd>
</dl>

缩写:

1
2
3
<p>
最近大家都在学 <abbr title="Hypertext Markup Language">HTML</abbr>。
</p>

时间:<time datetime="2016-01-20">2016年1月20日</time>

特殊字符:

符号 代码
< <
> >
"
'
& &
空格  

title 与 h1 的区别、b 与 strong 的区别、i 与 em 的区别?

title属性没有明确意义只表示是个标题,H1则表示层次明确的标题,对页面信息的抓取也有很大的影响;

strong是标明重点内容,有语气加强的含义,使用阅读设备阅读网络时:<strong>会重读,而<b>是展示强调内容。

i内容展示为斜体,em表示强调的文本;

自然样式标签:

b, i,u,s, pre

语义样式标签:

strong, em, ins, del, code

应该准确使用语义样式标签, 但不能滥用, 如果不能确定时首选使用自然样式标签。

图片

普通图片:

1
2
<!-- 显示 Vue.js logo -->
<img src="https://vuejs.org/images/logo.png" alt="Vue.js logo" width="200">

带说明的图片(比如博客头像):

1
2
3
4
5
6
7
<figure>
<img src="https://c-ssl.duitang.com/uploads/item/201608/12/20160812105654_JaZUi.thumb.700_0.jpeg"
alt="我是说明,小新"
width="60"
height="60">
<figcaption>小新</figcaption>
</figure>

表格

简单表格:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<table>
<thead>
<tr>
<th>球员</th>
<th>号码</th>
<th>身高</th>
<th>体重</th>
<th>生日</th>
<th>国籍</th>
<th>出生地</th>
<tr>
</thead>
<tbody>
<tr>
<td>勒布朗·詹姆斯</td>
<td>23</td>
<td>2.03米/6英尺8英寸</td>
<td>113.4公斤</td>
<td>1984年12月30日</td>
<td colspan="2">美国</td>
</tr>
<tr>
<td>凯里·欧文</td>
<td>2</td>
<td>1.91米/6英尺3英寸</td>
<td>88公斤/193磅</td>
<td>1992年3月23日</td>
<td>美国/澳洲</td>
<td>澳洲</td>
</tr>
<tr>
<td>凯文·乐福</td>
<td>0</td>
<td>2.08米/6英尺10英寸</td>
<td>110公斤/243磅</td>
<td>1988年9月7日</td>
<td colspan="2">美国</td>
</tr>
</tbody>
</table>

<style>
table {
border-collapse: collapse;
//表格线合并
}
th, td {
border: 1px solid green;
//设置边框
}
</style>

表单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<div class="login">
//设置登录框
<input type="text" name="sex" placeholder="可以显示未输入前的字">
//输入框
<form action="/getInfo" method="get">
<div class="submit">
<button>提交😁</button>
</div>
<div class="username">
<label for="username">姓名</label>
<input id="username" type="text" name="username" value="ruo">
//文本类型输入框
</div>
<div class="password">
<label for="password">密码</label>
<input id="password" type="password" name="password" placeholder="输入密码">
//密码类型输入框
</div>
<div class="hobby">
<label>爱好</label>
<input type="checkbox" name="hobby" value="read"> 读书
<input type="checkbox" name="hobby" value="music"> 听歌
<input type="checkbox" name="hobby" value="study"> 学习
//多选框(复选框)
</div>
<div class="sex">
<label>性别</label>
<input type="radio" name="sex" value="男"> 男
<input type="radio" name="sex" value="女"> 女
//单选框
</div>
<div class="file">
<input type="file" name="myfile" accept="image/png">
//上传文件按钮
</div>
<div class="select">
<select name="city">
<option value="beijing">北京</option>
<option value="shanghai" selected>上海</option>
<option value="hangzhou">杭州</option>
</select>
//下拉框,默认上海
</div>
<div class="textarea">
<textarea name="article">
多行文本,注意和 type=text的区别
</textarea>
//下面是四种按钮提交方式
<input type="hidden" name="csrf" value="12345623fafdffdd">
<input type="button" value="Buttom" /> 不会提交
<input type="submit" value="Submit" /> 会提交
<input type="reset" value="Reset" /> 重置输入
</div>
</form>
</div>

表单提交中GetPost方式的区别

  1. Get一般用于从服务器上获取数据,Post向服务器传送数据
  2. Get传输的数据是拼接在 Url 之后的,对用户是可见的;Post的传输数据对用户是不可见的
  3. Get传送的数据量较小,不能大于 2KB。Post传送的数据量较大,一般被默认为不受限制
  4. Get安全性非常低,Post安全性较高
  5. 在 FORM 提交的时候,如果不指定 Method,则默认为Get请求

label 标签的作用

label 标签来定义表单控制间的关系,当用户选择该标签时,浏览器会自动将焦点转到和标签相关的表单控件上。

超链接

1
2
3
4
5
6
7
8
9
//简单链接:
<a href="https://github.com/fe13">FE 13</a>
//title属性:
<a href="https://github.com/fe13" title="可能是未来中国最火的前端工程师的聚集地">FE 13</a>
//外部链接:
//将target设置成_blank时,点击链接浏览器会新开一个 Tab 打开该网页。
<a href="https://github.com/fe13" title="可能是未来中国最火的前端工程师的聚集地" target="_blank">FE 13</a>
//返回顶部链接:
<a href="#">返回页面顶部</a>

文档内部链接:

用于定位到文档的某一部分,<a> 的 href 要对应文档内某个元素的 id(id 的值在文档内要唯一)

1
2
<a href="#email链接">Email链接</a>
<a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element#Forms">表单相关标签</a>

图片链接:

1
2
3
<a href="https://github.com/fe13" title="可能是未来中国最火的前端工程师的聚集地">
<img src="https://avatars0.githubusercontent.com/u/28950695?v=3&s=200" height="100">
</a>
1
2
3
4
5
6
下载链接:
<a href="https://angular.io/resources/images/logos/angular/angular.svg" download>下载 Angular Logo</a>
电话链接:
<a href="tel:+8613701234567">+86 13701234567</a>
Email 链接:
<a href="mailto:xiaoxin@xiaoxin.cn">发封邮件给小新</a> <br>

文档结构

页头 <header>

导航栏 <nav>

主内容 <main>,后面一般接 <article>,<section>,<div>

侧边栏 <aside>

页尾 <footer>

语义化 HTML

选择合适的标签、使用合理的代码结构,便于开发者和视觉障碍人士阅读,同时让浏览器的爬虫和机器很好地解析。

相对路径

同一站点尽量使用相对路径。

1
2
3
4
5
6
<!-- index.html -->
<img src="images/logo.png">
/* app.css */
.container {
background: url('../images/background.png');
}

绝对路径

https://github.com/fe13/fe/blob/master/README.md

浏览器内核

主要分成两部分:渲染引擎(layout engineer 或 Rendering Engine)和 JS 引擎。

渲染引擎:负责取得网页的内容(HTML、XML、图像等等)、整理讯息(例如加入 CSS 等),以及计算网页的显示方式,然后会输出至显示器或打印机。浏览器的内核的不同对于网页的语法解释会有不同,所以渲染的效果也不相同。所有网页浏览器、电子邮件客户端以及其它需要编辑、显示网络内容的应用程序都需要内核。

JS引擎:解析和执行 javascript 来实现网页的动态效果。

最开始渲染引擎和 JS 引擎并没有区分的很明确,后来 JS 引擎越来越独立,内核就倾向于只指渲染引擎。

常见的浏览器内核有哪些?

Trident内核:IE,MaxThon,TT,The World,360,搜狗浏览器等。[又称 MSHTML]

Gecko内核:Netscape6 及以上版本,FF,MozillaSuite/SeaMonkey 等

Presto内核:Opera7 及以上。      [Opera 内核原为:Presto,现为:Blink;]

Webkit内核:Safari,Chrome 等。   [ Chrome 的:Blink(WebKit 的分支)]

HTML5 新特性

  1. 语义化标签: header footer main article nav section aside
  2. 增强型表单: date name email range search tel
  3. 视频音频: video audio
  4. canvas绘图, svg绘图
  5. 地理定位: geolocation
  6. 拖放 API: drag
  7. web worker: 是运行在后台的 js 脚本
  8. webstorage: localStorage sessionStorage
  9. webSocket: HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议

如何实现浏览器内多个标签页之间的通信

  1. 使用 localStorage: localStorage.setItem(key,value)localStorage.getItem(key)
  2. websocket 协议
  3. webworker