TypeScript
1.可兼容JavaScript应民;相比js始绍,加入了注釋胜茧;添加一個(gè)完整的類結(jié)構(gòu)睹簇,更好的面向?qū)ο螅?/p>
2.Mac OS X環(huán)境搭建:
2.1.安裝homebrew(套件管理器)官網(wǎng):brew.sh:
ruby -e "$(curl -fsSL
https://raw.githubusercontent.com/Homebrew/install/master/install)"
2.2安裝npm(nodejs包管理器):
brew install node
2.3.安裝typescript(安裝成功后掌眠,可以鍵入tsc測(cè)試):
npm install -g typescript
tsc
3.IDE中新建typescript文件蕾盯,添加watcher實(shí)現(xiàn)自動(dòng)編譯
也可以用命令的方式編譯一個(gè)ts文件為js文件:終端目錄下,tsc xx.ts蓝丙,會(huì)生成同名js文件
4.TypeScript 7種 基礎(chǔ)數(shù)據(jù)類型:
Boolean:
var isBone: boolean = false; //使用時(shí)要給個(gè)初始化的值
Number:
var height: number = 6;
String:
var name: string = "bob";
Array:
var list: number[] = [1, 2, 3];
// 或者
var list: Array<string> = [ "joe", ”kevin” ];
獲取數(shù)組中數(shù)據(jù):
List[0];
Enum:
枚舉類型:有一定范圍的數(shù)據(jù)可以用枚舉類型最好
enum Color {Red, Green, Blue};
var colorName:string = Color[1];
console...
枚舉類型還可以進(jìn)行(數(shù)組)下標(biāo)賦值操作:
enum Color {Red = 10, Green = 12, Blue = 13};
var colorName: string = Color[12]; // Green
// 獲取下標(biāo):
var c:Color = Color.Green; // 1
Any:
任意類型:可以多次賦值级遭,后面的會(huì)覆蓋前面的。
var notSure: any = 10;
notSure = "hello";
也可以指定類型渺尘,如下指定為數(shù)組
Var list: any[ ] = [1, “hello”, false];
List[2] // false
Void:
對(duì)函數(shù)進(jìn)行聲明:
function tell( ):string{
return "hello";
}
不需要任何返回值挫鸽,就可以把函數(shù)聲明為void
function tell1( ):void{}
類:
創(chuàng)建一個(gè)類:
class Preson{
// 屬性
name:string;
age:number;
// 構(gòu)造方法
constructor(name:string, age:number){
this.name = name;
this.age = age;
}
// 普通方法
print(){
return this.name + " : " + this.age;
}
}
調(diào)用這個(gè)類:
var p = new Person(“mike”, 10);
p.print();
類的繼承
無(wú)構(gòu)造函數(shù)的類的繼承:
class Person{
name: string;
age: number;
tell(){
return this.name + ":" + this.age;
}
}
class Student extends Person{
school: string;
tell(){
return this.name + ":" + this.age + ":" + this.school;
}
}
var s = new Student("捷克學(xué)院");
// 沒(méi)有構(gòu)造函數(shù),無(wú)法使用參數(shù)鸥跟,就用如下方式傳入?yún)?shù)值
s.name = "jack";
s.age = 23;
s.school = "捷克學(xué)院";
// 執(zhí)行
s.tell();
有構(gòu)造函數(shù)的類的繼承:
class Person{
name: string;
age: number;
constructor(name:string, age:number){
this.name = name;
this.age = age;
}
tell(){
return this.name + ":" + this.age;
}
}
class Student extends Person{
school: string;
constructor(school:string){
// 修改父類的參數(shù)丢郊,super要寫在最前面
super("ime", 23);
this.school = school;
}
tell(){
return this.name + ":" + this.age + ":" + this.school;
}
}
var s = new Student("捷克學(xué)院");
// s.name = "jack";
// s.age = 23;
// s.school = "捷克學(xué)院";
s.tell();
訪問(wèn)修飾符
公有:public(默認(rèn))
私有:private
class Person{
public name: string;
/**
* 默認(rèn)就是public盔沫,如果我在這里寫private,
* 或者在下面constructor參數(shù)中這樣寫:private name:string枫匾,
* 那么繼承自它的 Student 就無(wú)法訪問(wèn)到 name 這個(gè)屬性了
**/
age: number;
constructor(name:string, age:number){
this.name = name;
this.age = age;
}
tell(){
return this.name + ":" + this.age;
}
}
class Student extends Person{
school: string;
constructor(school:string){
this.school = school;
super("ime", 23);
}
tell(){
return this.name + ":" + this.age + ":" + this.school;
}
}
var s = new Student("捷克學(xué)院");
// s.name = "jack";
// s.age = 23;
// s.school = "捷克學(xué)院";
s.tell();
封裝的實(shí)現(xiàn)
· 利用private實(shí)現(xiàn)的私有屬性架诞,可以在類中通過(guò)getter和setter來(lái)對(duì)外開(kāi)發(fā)(私有屬性或方法)接口
· 在調(diào)用方法時(shí),getter和setter會(huì)默認(rèn)執(zhí)行
class Hello{
private _age:number;
show(){
return this._age;
}
get age():number{
// 可對(duì)外開(kāi)發(fā)私有屬性
return this._age;
}
set age(newage:number){
// 可修改私有屬性
if(newage >200 || newage < 0){
alert("請(qǐng)輸入正確的年齡");
}else{
this._age = newage;
}
}
}
var h = new Hello();
h.age = 300;
alert(h.tell());
static(靜態(tài))和其使用技巧
類的屬性或方法(非靜態(tài))干茉,可以通過(guò)實(shí)例化對(duì)象調(diào)用谴忧,如下:
class Person{
name:string;
tell(){
alert("姓名:" + this.name);
}
}
var p = new Person();
p.name = "hello";
p.tell();
但一旦屬性或方法聲明為Static后,就變?yōu)榱遂o態(tài)屬性 / 靜態(tài)方法
不能再通過(guò)實(shí)例化對(duì)象調(diào)用角虫,必須通過(guò)類本身調(diào)用:
class Person{
static name:string;
tell(){
alert("姓名:" + Person.name);
}
}
var p = new Person();
Person.name = "hello";
p.tell();
static靜態(tài)的使用(進(jìn)階)技巧:引用數(shù)據(jù)類型
class Greeter{
greeting:string;
constructor(msg:string){
this.greeting = msg;
}
greet(){
return "Hello," + this.greeting;
}
}
/**
創(chuàng)建一個(gè)類型沾谓,指定它的類型為當(dāng)前類的類型;
其實(shí)戳鹅,創(chuàng)建了一個(gè)類均驶,也就是創(chuàng)建了一個(gè)聲明,
可以把當(dāng)前數(shù)據(jù)的類型枫虏,聲明為當(dāng)前類的類型
我們稱它為“引用數(shù)據(jù)類型”
**/
var green:Greeter;
green = new Greeter("mike");
alert(green.greet());
函數(shù):函數(shù)類型
函數(shù)有 命名函數(shù) 和 匿名函數(shù)妇穴,在Ts中,可以指定函數(shù)參數(shù)的類型和函數(shù)本身(返回值)的類型:
// 命名函數(shù)
function add(x:number, y:number):string{
// 如果返回值的類型不是函數(shù)指定的類型模软,就會(huì)報(bào)錯(cuò)
// 例如:return x+y伟骨;就會(huì)報(bào)錯(cuò)
return "hello ts";// 類型相符燃异,不會(huì)報(bào)錯(cuò)
}
// 匿名函數(shù)(在js中,上面的命名函數(shù)是這種方式的語(yǔ)法糖)
var myadd = function(x:number, y:string):string{
return "hello ts";
}
上面的x继蜡,y,并不清楚其意義,我們可以讓參數(shù)更明確其意義:
/**
* "=>" 前面可以聲明參數(shù)的語(yǔ)義化名稱和類型
* "=>" 后面緊跟著函數(shù)類型
**/
var myAddts:(name:string, age:number) => number = function(n, a){
return a;
}
函數(shù):可選和默認(rèn)參數(shù)
可選參數(shù):用 钮蛛?表示碟绑,可傳可不傳
function buildName(firstName:string, lastName?:string){
if(lastName){
return fristName + " " + lastName;
} else {
return firstName;
}
}
// 均不會(huì)報(bào)錯(cuò)
var result1 = buildName("james", "bonde");
var result2 = buildName("bonde");
// 超出最大的參數(shù)個(gè)數(shù),則會(huì)報(bào)錯(cuò)
// var result3 = buildName("james", "bonde", "valin");
默認(rèn)參數(shù):直接在參數(shù)上賦值
function buildName(firstName:string, lastName="bonde"){
return firstName + " and " + lastName;
}
// 可以只傳一個(gè)參數(shù)碘举,也可以傳兩個(gè)參數(shù)忘瓦,傳兩個(gè),則會(huì)覆蓋默認(rèn)參數(shù)值
var result1 = buildName("Hello: ");
var result2 = buildName("james", "wade");
// 超出最大的參數(shù)個(gè)數(shù)引颈,仍舊會(huì)報(bào)錯(cuò)
// var result3 = buildName("james", "bonde", "valin");
函數(shù):可變參數(shù)
利用es6的擴(kuò)展運(yùn)算符(展開(kāi)運(yùn)算符)
function peopleName(firstName:string, ...restOfName:string[]){
// restOfName:string[] 代表數(shù)據(jù)類型為字符串的數(shù)組
return firstName + " " + restOfName.join(" ");
}
// 利用可變參數(shù)耕皮,可以傳遞無(wú)數(shù)個(gè)參數(shù),在需要傳入不確定參數(shù)個(gè)數(shù)時(shí)蝙场,很好用
var pn = peolpleName("jack", "mary", "honly", "kevin"...);
Lambads 和 this 關(guān)鍵字的使用
var people = {
name:["iwen","ime","if","bean"];
getName:function(){
return function(){
var i = Math.floor(Math.random()*4);
return {
n:this.name[i]
}
}
}
}
var myName = people.getName();
alert("名字:" + myName().n); // 這是訪問(wèn)不到的
此時(shí)這個(gè)this指向的不是people凌停。調(diào)用發(fā)現(xiàn)getName中的this關(guān)鍵字指向的是getName,訪問(wèn)不到外部的name屬性。
可以用lambads表達(dá)式"()=>"修改售滤,箭頭函數(shù)能保存函數(shù)創(chuàng)建時(shí)的 this值罚拟,而不是調(diào)用時(shí)的值台诗。
var people = {
name:["iwen","ime","if","bean"];
getName:function(){
return () => {
var i = Math.floor(Math.random()*4);
return {
// 這里的this代表函數(shù)創(chuàng)建時(shí)的this,因此可以訪問(wèn)到name屬性
n:this.name[i]
}
}
}
}
var myName = people.getName();
alert("名字:" + myName().n); // 此時(shí)可訪問(wèn)
TypeScript 的重載
function attr(name:string):string;
fucntion attr(age:number):number;
function attr(nameOrAge:any):any{
if(nameOrAge && typeof nameOrAge === "string"){
alert("姓名");
}else{
alert("年齡");
}
}
/**
* 在某些編程語(yǔ)言中赐俗,函數(shù)重載或方法重載是指能夠創(chuàng)建具有不同實(shí)現(xiàn)的同名方法拉队。
* 對(duì)重載函數(shù)的調(diào)用將運(yùn)行與調(diào)用上下文相適應(yīng)的該函數(shù)的特定實(shí)現(xiàn),
* 允許一個(gè)函數(shù)調(diào)用根據(jù)上下文執(zhí)行不同的任務(wù)阻逮。
* 這里如果有不清楚可以看一下這里:https://www.zhihu.com/question/63751258
**/
attr("Hello");
attr(19);
接口-創(chuàng)建接口
// 規(guī)范了參數(shù)的類型
function printeLabel(labelObj:{label:string}){
console.log(labelObj.label);
}
var myObj = {label:"Hello"};
// var myObj = {label:10};
printeLabel(myObj);
接口-可選屬性
interface USB{
name:string;
age:number;
}
function printUSB(pu:USB){
console.log(pu.name);
}
// 如果不定義參數(shù)可選粱快,my必須包含兩個(gè)參數(shù),即string類型的name和number類型的age夺鲜,否則編譯會(huì)報(bào)錯(cuò)
var my = {name: "ime", age: 100};
printUSB(my);
可選參數(shù)通過(guò) " 皆尔?"定義
interface USB{
name?:string;
age?:number;
}
function printUSB(pu:USB){
console.log(pu.name);
}
// 定義了參數(shù)可選,my就可以按照可選參數(shù)傳遞參數(shù)了币励。
var my = {name: "ime"};
printUSB(my);
接口-函數(shù)類型
用接口的類型 來(lái) 規(guī)定函數(shù)的類型
interface SearchFunc{
(source:string, subString:string):boolean;
}
// 定義函數(shù)的類型為接口的類型
var mySearch:SearchFunc:
mySearch = function(src:string, sub:string){
var result = src.search(sub);
if(result != -1){
return true;
}else{
return false;
}
}
接口-數(shù)組類型
使用接口對(duì)數(shù)組進(jìn)行規(guī)范化
interface StringArray{
/**
* 數(shù)組中index對(duì)應(yīng)的是一個(gè)number類型慷蠕,但數(shù)組最終返回的是一個(gè)string類型
* 可理解為下標(biāo)是number類型;數(shù)組中的數(shù)據(jù)是字符串類型
**/
[index:number]:string;
}
var myArray:StringArray;
// 規(guī)定了數(shù)組中必須是string類型食呻,其他類型都不可以流炕,即使為空,也必須是空字符串仅胞。
myArray = ["10", "12"];
alert(myArray[1]);
接口-class類型
interface ClockInterface{
currentTime:Date;
setTime(d:Date);
}
class Clock implements ClockInterface{
currentTime:Date;
setTime(d:Date){
this.currentTime = d;
}
constructor(h:number; m:number){
}
}
接口 - 接口繼承與混合類型
接口的繼承:
interface Shape{
color:string;
}
interface PenStroke{
penWidth:number;
}
interface Square extends Shape,PenStroke{
sideLength:number;
}
var s = <Square>{}; // 將一個(gè)接口初始化在一個(gè)變量中
s.color = "blue";
s.penWidth = 10;
s.sideLength = 10;
接口的混合類型:
interface Counter{
interval:number;
reset():void;
(start:number):string;
}
var c:Counter;
c(10);
c.reset();
...
認(rèn)識(shí)泛型
可以讓參數(shù)類型在使用時(shí)才指定類型每辟,而不需要在方法定義時(shí)就指定,這樣使用更加靈活干旧;它相比于any類型渠欺,在使用方法時(shí)有更好的明確性。
function Hello(num:number):number{
return num; // 必須是number類型
}
function hello(str:any):any{
return str; // 可以是任意類型
}
// 泛型一般用<T>表示,也可以用其他的大寫字母
function hello<T>(arg:T):T{
return arg; // 在調(diào)用之前可以是任意類型
}
// 調(diào)用時(shí)指定參數(shù)類型后椎眯,就必須是這種類型
var output = hello<string>("world");
泛型的使用
Vue
1挠将、談?wù)勀銓?duì)MVVM開(kāi)發(fā)模式的理解
Model:代表數(shù)據(jù)模型,數(shù)據(jù)和業(yè)務(wù)邏輯都在Model層中定義编整;
View:代表UI視圖舔稀,負(fù)責(zé)數(shù)據(jù)的展示;
ViewModel:負(fù)責(zé)監(jiān)聽(tīng)Model中數(shù)據(jù)的改變并且控制視圖的更新掌测,處理用戶交互操作内贮;
Model和View并無(wú)直接關(guān)聯(lián),而是通過(guò)ViewModel來(lái)進(jìn)行聯(lián)系的汞斧,Model和ViewModel之間有著雙向數(shù)據(jù)綁定的聯(lián)系夜郁。因此當(dāng)Model中的數(shù)據(jù)改變時(shí)會(huì)觸發(fā)View層的刷新,View中由于用戶交互操作而改變的數(shù)據(jù)也會(huì)在Model中同步断箫。
這種模式實(shí)現(xiàn)了Model和View的數(shù)據(jù)自動(dòng)同步拂酣,因此開(kāi)發(fā)者只需要專注對(duì)數(shù)據(jù)的維護(hù)操作即可,而不需要自己操作dom仲义。
2婶熬、Vue 有哪些指令剑勾?
v-html、v-text赵颅、v-show虽另、v-if、v-for等
3饺谬、v-if 和 v-show 的區(qū)別
v-show 僅僅控制元素的顯示方式捂刺,將 display 屬性在 block 和 none 來(lái)回切換;而v-if會(huì)控制這個(gè) DOM 節(jié)點(diǎn)的存在與否募寨。當(dāng)我們需要經(jīng)常切換某個(gè)元素的顯示/隱藏時(shí)族展,使用v-show會(huì)更加節(jié)省性能上的開(kāi)銷;當(dāng)只需要一次顯示或隱藏時(shí)拔鹰,使用v-if更加合理仪缸。
4、簡(jiǎn)述Vue的響應(yīng)式原理
當(dāng)一個(gè)Vue實(shí)例創(chuàng)建時(shí)列肢,vue會(huì)遍歷data選項(xiàng)的屬性恰画,用 Object.defineProperty 將它們轉(zhuǎn)為getter/setter并且在內(nèi)部追蹤相關(guān)依賴,在屬性被訪問(wèn)和修改時(shí)通知變化瓷马。 每個(gè)組件實(shí)例都有相應(yīng)的watcher程序?qū)嵗┗梗鼤?huì)在組件渲染的過(guò)程中把屬性記錄為依賴,之后當(dāng)依賴項(xiàng)的setter被調(diào)用時(shí)欧聘,會(huì)通知watcher重新計(jì)算片林,從而致使它關(guān)聯(lián)的組件得以更新。
用ES5實(shí)現(xiàn):
// 第一步 實(shí)現(xiàn)基本架構(gòu)
// 第二步 把模型的數(shù)據(jù) 顯示到視圖
// 第三步 更新視圖同步到模型怀骤,再更新視圖
// 發(fā)布者
class Vue{
constructor(options){
this.options = options;
this.$data = options.data;
this.$el = document.querySelector(options,el);
this._directives = {
// 存放訂閱者集合的數(shù)組對(duì)象
}
this.Observer(this.$data);
this.Compile(this.$el);
}
// 劫持?jǐn)?shù)據(jù)
Observer(data){
// 更新視圖 局部更新
for(let key in data){
this._driectives[key] = []; // 空類數(shù)組對(duì)象
let val = data[key]; // 當(dāng)前的值
let _this = this;
Object.defineProperty(this.$data, key, {
get: function(){
return val;
},
set: function(newVal){
if(newVal !== val){
val = newVal;
//_this._directives[key] 得到myText的數(shù)組
_this._directives[key].forEach(watch=>{
// 遍歷訂閱者的實(shí)例
watch.update(); // 更新視圖
})
}
}
});
}
}
// 解析指令
Compile(el){
let nodes = el.children;] // 獲取APP下面的子元素
for(let i = 0;i < nodes.length; i++){
let node = nodes[i]; // 當(dāng)前元素
if(node.children.length){
// 如果當(dāng)前元素含有子元素拇厢,就遞歸調(diào)用自己
this.Compile(node);
}
if(node.hasAttribute('v-text')){
// 獲取屬性值,對(duì)號(hào)加入對(duì)應(yīng)的訂閱者數(shù)組
let attrVal = node.getAttribute("v-text")
this._directives[attrVal].push(new Watcher(node, attrVal, this, 'innerHTML'));
}
if(node.hasAttribute('v-model')){
let attrVal = node.getAttribute("v-model");
this._directives[attrVal].push(new Watcher(node, attrVal, this, 'value'));
// 監(jiān)聽(tīng)文本框事件
node.addEventListener('input', (function(){
return function(){
// console.log(node.value);
// 更新視圖到模型
this.$data[attrVal] = node.value;
}
})());
}
}
}
class Watcher{
constructor(el, vm, mySelf, attr){
this.el = el;
this.el = vm;
this.el = mySelf;
this.el = attr;
this.update(); // 初始化數(shù)據(jù)
}
update(){
// div對(duì)象[innerHTML] = vue對(duì)象.data['myText']
// input對(duì)象[value] = vue對(duì)象.data['myText']
this.el[this.attr] = this.mySelf.$data[this.vm];
}
}
}
在Vue3.0版本中式采用ES6的Proxy對(duì)象來(lái)實(shí)現(xiàn)晒喷。
// 獲取段落節(jié)點(diǎn)
const paragraph = document.getElementById('pararaph');
// 獲取輸入框節(jié)點(diǎn)
const input = document.getElementById('input');
// 需要代理的數(shù)據(jù)對(duì)象
const data = {
text: 'hello world'
}
const handler = {
// 監(jiān)控data中的text屬性變化
set: function(target, prop, value){
if(prop === 'text'){
// 更新值
target[prop] = value;
// 更新試圖
paragraph.innerHTML = value;
input.value = value;
retur true;
} else {
return false;
}
}
}
// 構(gòu)造 proxy 對(duì)象
const myText = new Proxy(data, handler);
// 添加input 監(jiān)聽(tīng)事件
input.addEventListener('input', function(e){
myText.text = e.target.value; // 更新myText的值
});
初始化值
myText.text = data.text;
5、Vue中如何在組件內(nèi)部實(shí)現(xiàn)一個(gè)雙向數(shù)據(jù)綁定访敌?
假設(shè)有一個(gè)輸入框組件凉敲,用戶輸入時(shí),同步父組件頁(yè)面中的數(shù)據(jù)寺旺。
具體思路:父組件通過(guò)props傳值給子組件爷抓,子組件通過(guò) $emit 來(lái)通知父組件修改相應(yīng)的props值,具體實(shí)現(xiàn)如下:
import Vue from 'vue'
const component = {
props: ['value'],
template: ` < div > <input type = "text"@input = "handleInput": value = "value" > </div>`,
data(){
return{}
},
methods:{
handleInput(e){
this.$emit('input',e.target.value)
}
}
}
new Vue({
components:{CompOne:component},
el:'#root',template:`<div><comp-one:value1="value"@input="value = arguments[0]"></comp - one > </div>`,
data(){
return{value:'123'}
}
})
6阻塑、Vue中v-model的實(shí)現(xiàn)原理是怎樣的蓝撇?
當(dāng)一個(gè)Vue實(shí)例創(chuàng)建時(shí),vue會(huì)遍歷data選項(xiàng)的屬性陈莽,用 Object.defineProperty 將它們轉(zhuǎn)為getter/setter并且在內(nèi)部追蹤相關(guān)依賴渤昌,在屬性被訪問(wèn)和修改時(shí)通知變化虽抄。 每個(gè)組件實(shí)例都有相應(yīng)的watcher程序?qū)嵗鼤?huì)在組件渲染的過(guò)程中把屬性記錄為依賴独柑,之后當(dāng)依賴項(xiàng)的setter被調(diào)用時(shí)迈窟,會(huì)通知watcher重新計(jì)算,從而致使它關(guān)聯(lián)的組件得以更新
7忌栅、Vue中如何監(jiān)控某個(gè)屬性值的變化车酣?
比如現(xiàn)在需要監(jiān)控data中, obj.a 的變化索绪。Vue中監(jiān)控對(duì)象屬性的變化你可以這樣:
watch: {
obj: {
handler(newValue, oldValue) {
console.log('obj changed')
},
deep: true
}
}
deep屬性表示深層遍歷湖员,但是這么寫會(huì)監(jiān)控obj的所有屬性變化,并不是我們想要的效果瑞驱,所以做點(diǎn)修改:
watch: {
'obj.a': {
handler(newName, oldName) {
console.log('obj.a changed')
}
}
}
還有一種方法娘摔,可以通過(guò)computed 來(lái)實(shí)現(xiàn),只需要:
// 利用計(jì)算屬性的特性來(lái)實(shí)現(xiàn)钱烟,當(dāng)依賴改變時(shí)晰筛,便會(huì)重新計(jì)算一個(gè)新值。
computed: {
a1() {
return this.obj.a
}
}
8拴袭、Vue中給data中的對(duì)象屬性添加一個(gè)新的屬性時(shí)會(huì)發(fā)生什么读第,如何解決?
<template>
<div>
<ul>
<li v-for="value in obj" :key="value">
{{value}}
</li>
</ul>
<button @click="addObjB">
添加obj.b
</button>
</div>
</template>
<script>
export default {
data() {
return {
obj:
{
a: 'obj.a'
}
}
},
methods: {
addObjB() {
this.obj.b = 'obj.b'console.log(this.obj)
}
}
}
</script>
<style>
</style>
點(diǎn)擊button會(huì)發(fā)現(xiàn)拥刻, obj.b 已經(jīng)成功添加怜瞒,但是視圖并未刷新:
原因在于在Vue實(shí)例創(chuàng)建時(shí), obj.b 并未聲明般哼,因此就沒(méi)有被Vue轉(zhuǎn)換為響應(yīng)式的屬性吴汪,自然就不會(huì)觸發(fā)視圖的更新,這時(shí)就需要使用Vue的全局api—— $set():
// $set() 方法相當(dāng)于手動(dòng)的去把 obj.b 處理成一個(gè)響應(yīng)式的屬性蒸眠,此時(shí)視圖也會(huì)跟著改變了:
addObjB() {
this.$set(this.obj, 'b', 'obj.b')
console.log(this.obj)
}
視圖改變:
9漾橙、delete和Vue.delete刪除數(shù)組的區(qū)別
delete只是被刪除的元素變成了 empty/undefined 其他的元素的鍵值還是不變。
Vue.delete / this.$delete 直接刪除了數(shù)組 改變了數(shù)組的鍵值楞卡。
var a = [1, 2, 3, 4]
var b = [1, 2, 3, 4]
delete a[1]
console.log(a)
this.$delete(b, 1)
console.log(b)
10霜运、如何優(yōu)化SPA應(yīng)用的首屏加載速度慢的問(wèn)題?
· 將公用的JS庫(kù)通過(guò)script標(biāo)簽外部引入蒋腮,減小 app.bundel 的大小淘捡,讓瀏覽器并行下載資源文件,提高下載速度池摧;
· 在配置 路由時(shí)焦除,頁(yè)面和組件使用懶加載的方式引入,進(jìn)一步縮小 app.bundel 的體積作彤,在調(diào)用某個(gè)組件時(shí)再加載對(duì)應(yīng)的js文件膘魄;
· 加一個(gè)首屏loading圖乌逐,提升用戶體驗(yàn);
11瓣距、前端如何優(yōu)化網(wǎng)站性能黔帕?
1、減少 HTTP 請(qǐng)求數(shù)量
- css sprites
- 合并css和js文件蹈丸,壓縮
- 采用lazyLoad懶加載
2成黄、控制資源文件加載優(yōu)先級(jí)
3、利用瀏覽器緩存
4逻杖、減少重排(Reflow)
- 如果需要在DOM操作時(shí)添加樣式奋岁,盡量使用增加class屬性,而不是通過(guò)style操作樣式荸百。
5闻伶、減少 DOM 操作
6、圖標(biāo)使用 IconFont 替換
12够话、網(wǎng)頁(yè)從輸入網(wǎng)址到渲染完成經(jīng)歷了哪些過(guò)程蓝翰?
- 輸入網(wǎng)址;
- 發(fā)送到DNS服務(wù)器女嘲,并獲取域名對(duì)應(yīng)的web服務(wù)器對(duì)應(yīng)的ip地址畜份;
- 與web服務(wù)器建立TCP連接;
- 瀏覽器向web服務(wù)器發(fā)送http請(qǐng)求欣尼;
- web服務(wù)器響應(yīng)請(qǐng)求爆雹,并返回指定url的數(shù)據(jù)(或錯(cuò)誤信息,或重定向的新的url地址)愕鼓;
- 瀏覽器下載web服務(wù)器返回的數(shù)據(jù)及解析html源文件露懒;
- 生成DOM樹(shù)黔攒,解析css和js,渲染頁(yè)面订雾,直至顯示完成撩炊;
Vue的生命周期
beforeCreate(創(chuàng)建前)蟹但,在數(shù)據(jù)觀測(cè)和初始化事件還未開(kāi)始
created(創(chuàng)建后)恕刘,完成數(shù)據(jù)觀測(cè)桑嘶,屬性和方法的運(yùn)算,初始化事件册着, $el 屬性還沒(méi)有顯示出來(lái)
beforeMount(載入前),在掛載開(kāi)始之前被調(diào)用脾歧,相關(guān)的render函數(shù)首次被調(diào)用甲捏。實(shí)例已完成以下的配置:編譯模板,把data里面的數(shù)據(jù)和模板生成html鞭执。注意此時(shí)還沒(méi)有掛載html到頁(yè)面上司顿。
mounted(載入后)芒粹,在el 被新創(chuàng)建的 vm.$el 替換,并掛載到實(shí)例上去之后調(diào)用大溜。實(shí)例已完成以下的配置:用上面編譯好的html內(nèi)容替換el屬性指向的DOM對(duì)象化漆。完成模板中的html渲染到html頁(yè)面中。此過(guò)程中進(jìn)行ajax交互钦奋。
beforeUpdate(更新前)座云,在數(shù)據(jù)更新之前調(diào)用,發(fā)生在虛擬DOM重新渲染和打補(bǔ)丁之前付材‰希可以在該鉤子中進(jìn)一步地更改狀態(tài),不會(huì)觸發(fā)附加的重渲染過(guò)程厌衔。
updated(更新后)璧帝,在由于數(shù)據(jù)更改導(dǎo)致的虛擬DOM重新渲染和打補(bǔ)丁之后調(diào)用。調(diào)用時(shí)富寿,組件DOM已經(jīng)更新睬隶,所以可以執(zhí)行依賴于DOM的操作。然而在大多數(shù)情況下页徐,應(yīng)該避免在此期間更改狀態(tài)苏潜,因?yàn)檫@可能會(huì)導(dǎo)致更新無(wú)限循環(huán)。該鉤子在服務(wù)器端渲染期間不被調(diào)用泞坦。
beforeDestroy(銷毀前)窖贤,在實(shí)例銷毀之前調(diào)用。實(shí)例仍然完全可用贰锁。
destroyed(銷毀后)赃梧,在實(shí)例銷毀之后調(diào)用。調(diào)用后豌熄,所有的事件監(jiān)聽(tīng)器會(huì)被移除授嘀,所有的子實(shí)例也會(huì)被銷毀。該鉤子在服務(wù)器端渲染期間不被調(diào)用锣险。
什么是vue生命周期蹄皱?
Vue 實(shí)例從創(chuàng)建到銷毀的過(guò)程,就是生命周期芯肤。從開(kāi)始創(chuàng)建巷折、初始化數(shù)據(jù)、編譯模板崖咨、掛載Dom→渲染锻拘、更新→渲染、銷毀等一系列過(guò)程,稱之為 Vue 的生命周期署拟。
2婉宰、vue生命周期的作用是什么?
它的生命周期中有多個(gè)事件鉤子推穷,讓我們?cè)诳刂普麄€(gè)Vue實(shí)例的過(guò)程時(shí)更容易形成好的邏輯心包。
3、vue生命周期總共有幾個(gè)階段馒铃?
它可以總共分為8個(gè)階段:創(chuàng)建前/后蟹腾、載入前/后、更新前/后骗露、銷毀前/銷毀后岭佳。
4、第一次頁(yè)面加載會(huì)觸發(fā)哪幾個(gè)鉤子萧锉?
beforeCreate珊随、created、beforeMount柿隙、mounted 叶洞。
5、DOM 渲染在哪個(gè)周期中就已經(jīng)完成禀崖?
mounted
Vue實(shí)現(xiàn)數(shù)據(jù)雙向綁定的原理: Object.defineProperty()
1衩辟、首先Object.defineProperty()的用法:
Object.defineProperty() 方法會(huì)直接在一個(gè)對(duì)象上定義一個(gè)新屬性,或者修改一個(gè)對(duì)象的現(xiàn)有屬性波附, 并返回這個(gè)對(duì)象艺晴。
語(yǔ)法:Object.defineProperty(obj, prop, descriptor)
obj:要在其上定義屬性的對(duì)象。
prop:要定義或修改的屬性的名稱掸屡。
descriptror:將被定義或修改的屬性描述符封寞。
2、vue實(shí)現(xiàn)數(shù)據(jù)雙向綁定主要是:
采用數(shù)據(jù)劫持結(jié)合發(fā)布者-訂閱者模式的方式仅财,通過(guò) Object.defineProperty() 來(lái)劫持各個(gè)屬性的setter狈究,getter,在數(shù)據(jù)變動(dòng)時(shí)發(fā)布消息給訂閱者盏求,觸發(fā)相應(yīng)監(jiān)聽(tīng)回調(diào)抖锥。當(dāng)把一個(gè)普通 Javascript 對(duì)象傳給 Vue 實(shí)例來(lái)作為它的 data 選項(xiàng)時(shí),Vue 將遍歷它的屬性碎罚,用 Object.defineProperty() 將它們轉(zhuǎn)為 getter/setter磅废。用戶看不到 getter/setter,但是在內(nèi)部它們讓 Vue 追蹤依賴荆烈,在屬性被訪問(wèn)和修改時(shí)通知變化还蹲。
vue的數(shù)據(jù)雙向綁定 將MVVM作為數(shù)據(jù)綁定的入口,整合Observer,Compile和Watcher三者谜喊,通過(guò)Observer來(lái)監(jiān)聽(tīng)自己的model的數(shù)據(jù)變化,通過(guò)Compile來(lái)解析編譯模板指令(vue中是用來(lái)解析 {{}})倦始,最終利用watcher搭起observer和Compile之間的通信橋梁斗遏,達(dá)到數(shù)據(jù)變化 —>視圖更新;視圖交互變化(input)—>數(shù)據(jù)model變更雙向綁定效果鞋邑。
js通過(guò)Object.defineProperty()實(shí)現(xiàn)簡(jiǎn)單的雙向綁定:
<body>
<div id="app">
<input type="text" id="txt">
<p id="show">
</p>
</div>
</body>
<script type="text/javascript">
var obj = {};
Object.defineProperty(obj, 'txt', {
get: function (){
return obj
},
set: function (newValue) {
document.getElementById('txt').value = newValue;
document.getElementById('show').innerHTML = newValue
}
}) ;
document.addEventListener('keyup', function (e) {
obj.txt = e.target.value;
});
</script>
js通過(guò)Observer诵次、Compile、Watcher實(shí)現(xiàn)一個(gè)原生JS的雙向綁定:
// 第一步 實(shí)現(xiàn)基本架構(gòu)
// 第二步 把模型的數(shù)據(jù) 顯示到視圖
// 第三步 更新視圖同步到模型枚碗,再更新視圖
// 發(fā)布者
class Vue{
constructor(options){
this.options = options;
this.$data = options.data;
this.$el = document.querySelector(options,el);
this._directives = {
// 存放訂閱者集合的數(shù)組對(duì)象
}
this.Observer(this.$data);
this.Compile(this.$el);
}
// 劫持?jǐn)?shù)據(jù)
Observer(data){
// 更新視圖 局部更新
for(let key in data){
this._driectives[key] = []; // 空類數(shù)組對(duì)象
let val = data[key]; // 當(dāng)前的值
let _this = this;
Object.defineProperty(this.$data, key, {
get: function(){
return val;
},
set: function(newVal){
if(newVal !== val){
val = newVal;
//_this._directives[key] 得到myText的數(shù)組
_this._directives[key].forEach(watch=>{
// 遍歷訂閱者的實(shí)例
watch.update(); // 更新視圖
})
}
}
});
}
}
// 解析指令
Compile(el){
let nodes = el.children;] // 獲取APP下面的子元素
for(let i = 0;i < nodes.length; i++){
let node = nodes[i]; // 當(dāng)前元素
if(node.children.length){
// 如果當(dāng)前元素含有子元素逾一,就遞歸調(diào)用自己
this.Compile(node);
}
if(node.hasAttribute('v-text')){
// 獲取屬性值,對(duì)號(hào)加入對(duì)應(yīng)的訂閱者數(shù)組
let attrVal = node.getAttribute("v-text")
this._directives[attrVal].push(new Watcher(node, attrVal, this, 'innerHTML'));
}
if(node.hasAttribute('v-model')){
let attrVal = node.getAttribute("v-model");
this._directives[attrVal].push(new Watcher(node, attrVal, this, 'value'));
// 監(jiān)聽(tīng)文本框事件
node.addEventListener('input', (function(){
return function(){
// console.log(node.value);
// 更新視圖到模型
this.$data[attrVal] = node.value;
}
})());
}
}
}
class Watcher{
constructor(el, vm, mySelf, attr){
this.el = el;
this.vm= vm;
this.mySelf= mySelf;
this.attr= attr;
this.update(); // 初始化數(shù)據(jù)
}
update(){
// div對(duì)象[innerHTML] = vue對(duì)象.data['myText']
// input對(duì)象[value] = vue對(duì)象.data['myText']
this.el[this.attr] = this.mySelf.$data[this.vm];
}
}
}
Vue組件間的參數(shù)傳遞
1肮雨、父組件與子組件傳值
父組件傳給子組件:子組件通過(guò)props方法接受數(shù)據(jù)遵堵;
子組件傳給父組件: $emit 方法傳遞參數(shù)
2、非父子組件間的數(shù)據(jù)傳遞怨规,兄弟組件傳值
eventBus陌宿,就是創(chuàng)建一個(gè)事件中心,相當(dāng)于中轉(zhuǎn)站波丰,可以用它來(lái)傳遞事件和接收事件壳坪。項(xiàng)目比較小時(shí),用這個(gè)比較合適(雖然也有不少人推薦直接用VUEX掰烟,具體來(lái)說(shuō)看需求咯爽蝴。技術(shù)只是手段,目的達(dá)到才是王道)纫骑。
Vue的路由實(shí)現(xiàn):hash模式 和 history模式
hash模式:在瀏覽器中符號(hào)“#”蝎亚,#以及#后面的字符稱之為hash,用 window.location.hash 讀取惧磺。特點(diǎn):hash雖然在URL中颖对,但不被包括在HTTP請(qǐng)求中;用來(lái)指導(dǎo)瀏覽器動(dòng)作磨隘,對(duì)服務(wù)端安全無(wú)用缤底,hash不會(huì)重加載頁(yè)面。
history模式:history采用HTML5的新特性番捂;且提供了兩個(gè)新方法: pushState()个唧, replaceState()可以對(duì)瀏覽器歷史記錄棧進(jìn)行修改,以及popState事件的監(jiān)聽(tīng)到狀態(tài)變更设预。
Vue與Angular以及React的區(qū)別徙歼?
1、與AngularJS的區(qū)別
相同點(diǎn):都支持指令:內(nèi)置指令和自定義指令;都支持過(guò)濾器:內(nèi)置過(guò)濾器和自定義過(guò)濾器魄梯;都支持雙向數(shù)據(jù)綁定桨螺;都不支持低端瀏覽器。
不同點(diǎn):AngularJS的學(xué)習(xí)成本高酿秸,比如增加了Dependency Injection特性灭翔,而Vue.js本身提供的API都比較簡(jiǎn)單、直觀辣苏;在性能上肝箱,AngularJS依賴對(duì)數(shù)據(jù)做臟檢查,所以Watcher越多越慢稀蟋;Vue.js使用基于依賴追蹤的觀察并且使用異步隊(duì)列更新煌张,所有的數(shù)據(jù)都是獨(dú)立觸發(fā)的。
2退客、與React的區(qū)別
相同點(diǎn):React采用特殊的JSX語(yǔ)法骏融,Vue.js在組件開(kāi)發(fā)中也推崇編寫.vue特殊文件格式,對(duì)文件內(nèi)容都有一些約定井辜,兩者都需要編譯后使用绎谦;中心思想相同:一切都是組件,組件實(shí)例之間可以嵌套粥脚;都提供合理的鉤子函數(shù)窃肠,可以讓開(kāi)發(fā)者定制化地去處理需求;都不內(nèi)置列數(shù)AJAX刷允,Route等功能到核心包冤留,而是以插件的方式加載;在組件開(kāi)發(fā)中都支持mixins的特性树灶。
不同點(diǎn):React采用的Virtual DOM會(huì)對(duì)渲染出來(lái)的結(jié)果做臟檢查纤怒;Vue.js在模板中提供了指令,過(guò)濾器等天通,可以非常方便泊窘,快捷地操作Virtual DOM。
vue路由的鉤子函數(shù)
首頁(yè)可以控制導(dǎo)航跳轉(zhuǎn)像寒,beforeEach烘豹,afterEach等,一般用于頁(yè)面title的修改诺祸。一些需要登錄才能調(diào)整頁(yè)面的重定向功能携悯。
beforeEach主要有3個(gè)參數(shù)to,from筷笨,next典勇。
to:route即將進(jìn)入的目標(biāo)路由對(duì)象。
from:route當(dāng)前導(dǎo)航正要離開(kāi)的路由炒嘲。
next:function一定要調(diào)用該方法resolve這個(gè)鉤子。執(zhí)行效果依賴next方法的調(diào)用參數(shù)昌跌。可以控制網(wǎng)頁(yè)的跳轉(zhuǎn)照雁。
vuex是什么避矢?怎么使用?哪種功能場(chǎng)景使用它囊榜?
只用來(lái)讀取的狀態(tài)集中放在store中; 改變狀態(tài)的方式是提交mutations亥宿,這是個(gè)同步的事物卸勺; 異步邏輯應(yīng)該封裝在action中。
在main.js引入store烫扼,注入曙求。新建了一個(gè)目錄store,… export 映企。
場(chǎng)景有:?jiǎn)雾?yè)應(yīng)用中悟狱,組件之間的狀態(tài)、音樂(lè)播放堰氓、登錄狀態(tài)挤渐、加入購(gòu)物state
Vuex 使用單一狀態(tài)樹(shù),即每個(gè)應(yīng)用將僅僅包含一個(gè)store 實(shí)例,但單一狀態(tài)樹(shù)和模塊化并不沖突双絮。存放的數(shù)據(jù)狀態(tài)浴麻,不可以直接修改里面的數(shù)據(jù)。
mutations
mutations定義的方法動(dòng)態(tài)修改Vuex 的 store 中的狀態(tài)或數(shù)據(jù)囤攀。
getters
類似vue的計(jì)算屬性软免,主要用來(lái)過(guò)濾一些數(shù)據(jù)。
action
actions可以理解為通過(guò)將mutations里面處里數(shù)據(jù)的方法變成可異步的處理數(shù)據(jù)的方法焚挠,簡(jiǎn)單的說(shuō)就是異步操作數(shù)據(jù)膏萧。view 層通過(guò) store.dispath 來(lái)分發(fā) action。
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count ++
}
},
actions: {
increment (context) {
context.commit('increment');
}
}
})
modules
項(xiàng)目特別復(fù)雜的時(shí)候蝌衔,可以讓每一個(gè)模塊擁有自己的state榛泛、mutation、action胚委、getters挟鸠,使得結(jié)構(gòu)非常清晰,方便管理亩冬。
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleC = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB,
c: moduleC
}
})
其他知識(shí)點(diǎn):
1艘希、css只在當(dāng)前組件起作用
答:在style標(biāo)簽中寫入scoped即可 例如: <stylescoped></style>
2硼身、v-if 和 v-show 區(qū)別
答:v-if按照條件是否渲染,v-show是display的block或none覆享;
3佳遂、router的區(qū)別
router是“路由實(shí)例”對(duì)象包括了路由的跳轉(zhuǎn)方法煤搜,鉤子函數(shù)等。
http 和 https
HTTPS(Secure Hypertext Transfer Protocol)安全超文本傳輸協(xié)議:
它是一個(gè)安全通信通道唧席,它基于HTTP開(kāi)發(fā)擦盾,用于在客戶計(jì)算機(jī)和服務(wù)器之間交換信息,它使用安全套接字層(SSL)進(jìn)行信息交換淌哟,簡(jiǎn)單來(lái)說(shuō)它是HTTP的安全版迹卢。它是由Netscape開(kāi)發(fā)并內(nèi)置于其瀏覽器中,用于對(duì)數(shù)據(jù)進(jìn)行壓縮和解壓操作徒仓,并返回網(wǎng)絡(luò)上傳送回的結(jié)果腐碱。
HTTPS實(shí)際上應(yīng)用了Netscape的安全套接字層(SSL)作為HTTP應(yīng)用層的子層。(HTTPS使用端口443蓬衡,而不是象HTTP那樣使用端口80來(lái)和TCP/IP進(jìn)行通信喻杈。)SSL使用40 位關(guān)鍵字作為RC4流加密算法,這對(duì)于商業(yè)信息的加密是合適的狰晚。
HTTPS和SSL支持使用X.509數(shù)字認(rèn)證筒饰,如果需要的話用戶可以確認(rèn)發(fā)送者是誰(shuí)”谏梗總的來(lái)說(shuō)瓷们,HTTPS協(xié)議是由SSL+HTTP協(xié)議構(gòu)建的可進(jìn)行加密傳輸、身份認(rèn)證的網(wǎng)絡(luò)協(xié)議要比http協(xié)議安全秒咐。
在URL前加https://前綴表明是用SSL(安全套接字)加密的谬晕,你的電腦與服務(wù)器之間收發(fā)的信息傳輸將更加安全。 Web服務(wù)器啟用SSL需要獲得一個(gè)服務(wù)器證書(shū)并將該證書(shū)與要使用SSL的服務(wù)器綁定携取。
HTTPS和HTTP的區(qū)別:
https協(xié)議需要到ca申請(qǐng)證書(shū)攒钳,一般免費(fèi)證書(shū)很少,需要交費(fèi)雷滋。
http是超文本傳輸協(xié)議不撑,信息是明文傳輸文兢,https 則是具有安全性的ssl加密傳輸協(xié)議。
http和https使用的是完全不同的連接方式用的端口也不一樣,前者是80,后者是443焕檬。
http的連接很簡(jiǎn)單,是無(wú)狀態(tài)的姆坚。
HTTPS協(xié)議是由SSL+HTTP協(xié)議構(gòu)建的可進(jìn)行加密傳輸、身份認(rèn)證的網(wǎng)絡(luò)協(xié)議 要比http協(xié)議安全实愚。
詳細(xì)參看:http://www.reibang.com/p/6db0c6dc97a9
GET 和 POST
GET 和 POST 報(bào)文上的區(qū)別
先下結(jié)論兼呵,GET 和 POST 方法沒(méi)有實(shí)質(zhì)區(qū)別,只是報(bào)文格式不同腊敲。
GET 和 POST 只是 HTTP 協(xié)議中兩種請(qǐng)求方式击喂,而 HTTP 協(xié)議是基于 TCP/IP 的應(yīng)用層協(xié)議,無(wú)論 GET 還是 POST碰辅,用的都是同一個(gè)傳輸層協(xié)議茫负,所以在傳輸上,沒(méi)有區(qū)別乎赴。
報(bào)文格式上,不帶參數(shù)時(shí)潮尝,最大區(qū)別就是第一行方法名不同
POST方法請(qǐng)求報(bào)文第一行是這樣的
POST /uri HTTP/1.1 \r\n
GET方法請(qǐng)求報(bào)文第一行是這樣的
GET /uri HTTP/1.1 \r\n
是的榕吼,不帶參數(shù)時(shí)他們的區(qū)別就僅僅是報(bào)文的前幾個(gè)字符不同而已
帶參數(shù)時(shí)報(bào)文的區(qū)別呢? 在約定中勉失,GET 方法的參數(shù)應(yīng)該放在 url 中羹蚣,POST 方法參數(shù)應(yīng)該放在 body 中
舉個(gè)例子,如果參數(shù)是 name=chengqm, age=22乱凿。
GET 方法簡(jiǎn)約版報(bào)文是這樣的
GET /index.php?name=qiming.c&age=22 HTTP/1.1
Host: localhost
POST 方法簡(jiǎn)約版報(bào)文是這樣的
POST /index.php HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
name=qiming.c&age=22
現(xiàn)在我們知道了兩種方法本質(zhì)上是 TCP 連接顽素,沒(méi)有差別,也就是說(shuō)徒蟆,如果我不按規(guī)范來(lái)也是可以的胁出。我們可以在 URL 上寫參數(shù),然后方法使用 POST段审;也可以在 Body 寫參數(shù)全蝶,然后方法使用 GET。當(dāng)然寺枉,這需要服務(wù)端支持抑淫。
常見(jiàn)問(wèn)題:
GET 方法參數(shù)寫法是固定的嗎?
在約定中姥闪,我們的參數(shù)是寫在 ? 后面始苇,用 & 分割。
我們知道筐喳,解析報(bào)文的過(guò)程是通過(guò)獲取 TCP 數(shù)據(jù)催式,用正則等工具從數(shù)據(jù)中獲取 Header 和 Body函喉,從而提取參數(shù)。
也就是說(shuō)蓄氧,我們可以自己約定參數(shù)的寫法函似,只要服務(wù)端能夠解釋出來(lái)就行,一種比較流行的寫法是 http://www.example.com/user/name/chengqm/age/22喉童。POST 方法比 GET 方法安全撇寞?
按照網(wǎng)上大部分文章的解釋,POST 比 GET 安全堂氯,因?yàn)閿?shù)據(jù)在地址欄上不可見(jiàn)蔑担。
然而,從傳輸?shù)慕嵌葋?lái)說(shuō)咽白,他們都是不安全的啤握,因?yàn)?HTTP 在網(wǎng)絡(luò)上是明文傳輸?shù)模灰诰W(wǎng)絡(luò)節(jié)點(diǎn)上捉包晶框,就能完整地獲取數(shù)據(jù)報(bào)文排抬。
要想安全傳輸,就只有加密授段,也就是 HTTPS蹲蒲。GET 方法的長(zhǎng)度限制是怎么回事?
HTTP 協(xié)議沒(méi)有 Body 和 URL 的長(zhǎng)度限制届搁,對(duì) URL 限制的大多是瀏覽器和服務(wù)器的原因卡睦。
瀏覽器原因就不說(shuō)了漱抓,服務(wù)器是因?yàn)樘幚黹L(zhǎng) URL 要消耗比較多的資源,為了性能和安全(防止惡意構(gòu)造長(zhǎng) URL 來(lái)攻擊)考慮乞娄,會(huì)給 URL 長(zhǎng)度加限制补胚。POST 方法會(huì)產(chǎn)生兩個(gè)TCP數(shù)據(jù)包骚腥?
HTTP 協(xié)議中沒(méi)有明確說(shuō)明 POST 會(huì)產(chǎn)生兩個(gè) TCP 數(shù)據(jù)包束铭,而且實(shí)際測(cè)試(Chrome)發(fā)現(xiàn)带猴,header 和 body 不會(huì)分開(kāi)發(fā)送。
所以,header 和 body 分開(kāi)發(fā)送是部分瀏覽器或框架的請(qǐng)求方法涕侈,不屬于 post 必然行為木张。w3school 里面說(shuō) URL 的最大長(zhǎng)度是 2048 個(gè)字符
url 長(zhǎng)度限制是某些瀏覽器和服務(wù)器的限制技肩,和 HTTP 協(xié)議沒(méi)有關(guān)系。
跨域 —— 9種跨域方式實(shí)現(xiàn)原理
參考:https://mp.weixin.qq.com/s/nkDkofBidKQFp5oG4-aJNg
什么是跨域?
當(dāng)協(xié)議剧浸、子域名吨艇、主域名冯吓、端口號(hào)中任意一個(gè)不相同時(shí)凸舵,都算作不同域。不同域之間相互請(qǐng)求資源,就算作“跨域”。
同源策略
同源策略是一種約定摄咆,它是瀏覽器最核心也最基本的安全功能恶迈,如果缺少了同源策略步做,瀏覽器很容易受到 XSS、CSFR 等攻擊。所謂同源是指"協(xié)議+域名+端口"三者相同,即便兩個(gè)不同的域名指向同一個(gè) ip 地址一喘,也非同源。
同源策略限制內(nèi)容有:
Cookie、LocalStorage戳粒、IndexedDB 等存儲(chǔ)性內(nèi)容
DOM 節(jié)點(diǎn)
AJAX 請(qǐng)求發(fā)送后涂籽,結(jié)果被瀏覽器攔截了
但是有三個(gè)標(biāo)簽是允許跨域加載資源:
<img src=XXX>
<link href=XXX>
<script src=XXX>
請(qǐng)求跨域了树枫,那么請(qǐng)求到底發(fā)出去沒(méi)有斤吐?
跨域并不是請(qǐng)求發(fā)不出去庄呈,請(qǐng)求能發(fā)出去幌绍,服務(wù)端能收到請(qǐng)求并正常返回結(jié)果,只是結(jié)果被瀏覽器攔截了。
跨域解決方案:
(1)JSONP
(2)cors(后臺(tái)設(shè)置:簡(jiǎn)單請(qǐng)求和復(fù)雜請(qǐng)求)
(3)postMeassage
HTML5 XMLHttpRequest Level 2 中的 API寨辩,windows屬性,可用于解決:
- 頁(yè)面和其他打開(kāi)的新窗口的數(shù)據(jù)傳遞
- 多窗口之間消息傳遞
- 頁(yè)面與嵌套的iframe消息傳遞
- 上面三個(gè)場(chǎng)景的跨域數(shù)據(jù)傳遞
(4)websocket
實(shí)現(xiàn)了瀏覽器與服務(wù)器的全雙工通信缩多,websocket和http都是應(yīng)用層協(xié)議武契,基于TCP協(xié)議内颗,但是 WebSocket 是一種雙向通信協(xié)議恨溜,在建立連接之后躺盛,WebSocket 的 server 與 client 都能主動(dòng)向?qū)Ψ桨l(fā)送或接收數(shù)據(jù)周叮。
(5)node中間件代理(兩次跨域)
(6)nginx 反向代理
使用nginx反向代理實(shí)現(xiàn)跨域棕叫,是最簡(jiǎn)單的跨域方式急侥。
支持所有瀏覽器贝润,支持 session鹏秋,不需要修改任何代碼,并且不會(huì)影響服務(wù)器性能琴锭。
實(shí)現(xiàn)思路:通過(guò) nginx 配置一個(gè)代理服務(wù)器(域名與 domain1 相同蓖捶,端口不同)做跳板機(jī),反向代理訪問(wèn) domain2 接口皮获,并且可以順便修改 cookie 中 domain 信息萌京,方便當(dāng)前域 cookie 寫入比庄,實(shí)現(xiàn)跨域登錄制恍。
//porxy服務(wù)器
server {
listen 80;
server_name www.domain1.com;
location / {
proxy_pas http://www.domain2.com:8080; #反向代理
proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
index index.html index.htm;
#當(dāng)用webpack-dev-server等中間件代理接口訪問(wèn)nignx時(shí)鹃唯,此時(shí)無(wú)瀏覽器參與,故沒(méi)有同源限制,下面的跨域配置可不啟用
add_header Access-Control-Allow-Origin http://www.domain1.com; #當(dāng)前端只跨域不帶cookie時(shí),可為*
add_header Access-Control-Allow-Credentials true;
}
}
最后通過(guò)命令行nginx -s reload啟動(dòng) nginx
// index.html
var xhr = new XMLHttpRequest();
// 前端開(kāi)關(guān):瀏覽器是否讀寫cookie
xhr.withCredentials = true;
// 訪問(wèn)nginx中的代理服務(wù)器
xhr.open('get', 'http://www.domain1.com:81/?user=admin', true);
xhr.send();
// server.js
var http = require('http');
var server = http.createServer();
var qs = require('querystring');
server.on('request', function(req, res) {
var params = qs.parse(req.url.substring(2));
// 向前臺(tái)寫cookie
res.writeHead(200, {
'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly' // HttpOnly:腳本無(wú)法讀取
});
res.write(JSON.stringify(params));
res.end();
});
server.listen('8080');
console.log('Server is running at port 8080...');
(7)window.name + iframe
window.name 屬性的獨(dú)特之處:name 值在不同的頁(yè)面(甚至不同域名)加載后依舊存在仗谆,并且可以支持非常長(zhǎng)的 name 值(2MB)。
其中 a.html 和 b.html 是同域的指煎,都是http://localhost:3000;而 c.html 是http://localhost:4000
// a.html(http://localhost:3000/b.html)
<iframe src="http://localhost:4000/c.html" frameborder="0" onload="load()" id="iframe"></iframe>
<script>
let first = true
// onload事件會(huì)觸發(fā)2次枢纠,第1次加載跨域頁(yè),并留存數(shù)據(jù)于window.name
function load() {
if(first){
// 第1次onload(跨域頁(yè))成功后,切換到同域代理頁(yè)面
let iframe = document.getElementById('iframe');
iframe.src = 'http://localhost:3000/b.html';
first = false;
}else{
// 第2次onload(同域b.html頁(yè))成功后,讀取同域window.name中數(shù)據(jù)
console.log(iframe.contentWindow.name);
}
}
</script>
b.html 為中間代理頁(yè),與 a.html 同域,內(nèi)容為空。
// c.html(http://localhost:4000/c.html)
<script>
window.name = '我不愛(ài)你'
</script>
通過(guò) iframe 的 src 屬性由外域轉(zhuǎn)向本地域劲装,跨域數(shù)據(jù)即由 iframe 的 window.name 從外域傳遞到本地域。這個(gè)就巧妙地繞過(guò)了瀏覽器的跨域訪問(wèn)限制,但同時(shí)它又是安全操作佑附。
(8)location.hash + iframe
實(shí)現(xiàn)原理: a.html 欲與 c.html 跨域相互通信秃嗜,通過(guò)中間頁(yè) b.html 來(lái)實(shí)現(xiàn)恋沃。 三個(gè)頁(yè)面,不同域之間利用 iframe 的 location.hash 傳值,相同域之間直接 js 訪問(wèn)來(lái)通信。
具體實(shí)現(xiàn)步驟:一開(kāi)始 a.html 給 c.html 傳一個(gè) hash 值,然后 c.html 收到 hash 值后朱沃,再把 hash 值傳遞給 b.html,最后 b.html 將結(jié)果放到 a.html 的 hash 值中。
同樣的椭迎,a.html 和 b.html 是同域的,都是http://localhost:3000;而 c.html 是http://localhost:4000
// a.html
<iframe src="http://localhost:4000/c.html#iloveyou"></iframe>
<script>
window.onhashchange = function () { //檢測(cè)hash的變化
console.log(location.hash);
}
</script>
// b.html
<script>
window.parent.parent.location.hash = location.hash
//b.html將結(jié)果放到a.html的hash值中田盈,b.html可通過(guò)parent.parent訪問(wèn)a.html頁(yè)面
</script>
// c.html
console.log(location.hash);
let iframe = document.createElement('iframe');
iframe.src = 'http://localhost:3000/b.html#idontloveyou';
document.body.appendChild(iframe);
(9)document.domain + iframe
該方式只能用于二級(jí)域名相同的情況下畜号,比如 a.test.com 和 b.test.com 適用于該方式允瞧。
只需要給頁(yè)面添加 document.domain ='test.com' 表示二級(jí)域名都相同就可以實(shí)現(xiàn)跨域简软。
實(shí)現(xiàn)原理:兩個(gè)頁(yè)面都通過(guò) js 強(qiáng)制設(shè)置 document.domain 為基礎(chǔ)主域,就實(shí)現(xiàn)了同域述暂。
我們看個(gè)例子:頁(yè)面a.zf1.cn:3000/a.html獲取頁(yè)面b.zf1.cn:3000/b.html中 a 的值
// a.html
<body>
helloa
<iframe src="http://b.zf1.cn:3000/b.html" frameborder="0" onload="load()" id="frame"></iframe>
<script>
document.domain = 'zf1.cn'
function load() {
console.log(frame.contentWindow.a);
}
</script>
</body>
// b.html
<body>
hellob
<script>
document.domain = 'zf1.cn'
var a = 100;
</script>
</body>
總結(jié):
- CORS 支持所有類型的 HTTP 請(qǐng)求痹升,是跨域 HTTP 請(qǐng)求的根本解決方案
- JSONP 只支持 GET 請(qǐng)求,JSONP 的優(yōu)勢(shì)在于支持老式瀏覽器畦韭,以及可以向不支持 CORS 的網(wǎng)站請(qǐng)求數(shù)據(jù)疼蛾。
- 不管是 Node 中間件代理還是 nginx 反向代理,主要是通過(guò)同源策略對(duì)服務(wù)器不加限制艺配。
- 日常工作中察郁,用得比較多的跨域方案是 cors 和 nginx 反向代理
PWA
PWA全稱Progressive Web App,即 漸進(jìn)式WEB應(yīng)用转唉。
一個(gè) PWA 應(yīng)用首先是一個(gè)網(wǎng)頁(yè), 可以通過(guò) Web 技術(shù)編寫出一個(gè)網(wǎng)頁(yè)應(yīng)用. 隨后添加上 App Manifest 和 Service Worker 來(lái)實(shí)現(xiàn) PWA 的安裝和離線等功能
解決了哪些問(wèn)題绳锅?
可以添加至主屏幕,點(diǎn)擊主屏幕圖標(biāo)可以實(shí)現(xiàn)啟動(dòng)動(dòng)畫(huà)以及隱藏地址欄
實(shí)現(xiàn)離線緩存功能酝掩,即使用戶手機(jī)沒(méi)有網(wǎng)絡(luò)鳞芙,依然可以使用一些離線功能
實(shí)現(xiàn)了消息推送
它解決了上述提到的問(wèn)題,這些特性將使得 Web 應(yīng)用漸進(jìn)式接近原生 App期虾。
更多可參看:https://segmentfault.com/a/1190000012353473?utm_source=tag-newest
原型與原型鏈
什么是原型原朝?
在js中,所有對(duì)象都是Object的實(shí)例镶苞,并集成Object.prototype的屬性和方法喳坠,但是有一些是隱性的。
所有引用類型都具有對(duì)象特性茂蚓,可以自由擴(kuò)展屬性壕鹉。
var obj = {};
obj.attribute = "new attr";
var arr = [];
arr.attribute = "new attr";
function fn () {}
fn.attribute = "new attr";
所有的引用類型(包括數(shù)組,對(duì)象聋涨,函數(shù))都有隱性原型屬性(proto), 值也是一個(gè)普通的對(duì)象晾浴。
console.log(obj.__proto__);
所有的函數(shù),都有一個(gè) prototype 屬性牍白,值也是一個(gè)普通的對(duì)象脊凰。
console.log(obj.prototype);
所有的引用類型的proto屬性值都指向構(gòu)造函數(shù)的 prototype 屬性值。
console.log(obj.__proto__ === Object.prototype); // true
當(dāng)試圖獲取對(duì)象屬性時(shí)茂腥,如果對(duì)象本身沒(méi)有這個(gè)屬性狸涌,那就會(huì)去他的proto(prototype)中去尋找切省。
function Dog(name){
this.name = name;
}
Dog.prototype.callName = function (){
console.log(this.name,"wang wang");
}
let dog1 = new Dog("Three Mountain");
dog1.printName = function (){
console.log(this.name);
}
dog1.callName(); // Three Mountain wang wang
dog1.printName(); // Three Mountain
原型鏈圖:作用域及閉包
只有函數(shù)才能創(chuàng)造作用域。
for if else 不能創(chuàng)造作用域帕胆。
this:
本質(zhì)上來(lái)說(shuō)朝捆,在 js 里 this 是一個(gè)指向函數(shù)執(zhí)行環(huán)境的指針。this 永遠(yuǎn)指向最后調(diào)用它的對(duì)象懒豹,并且在執(zhí)行時(shí)才能獲取值芙盘,定義是無(wú)法確認(rèn)他的值。
var a = {
name : "A",
fn : function (){
console.log (this.name)
}
}
a.fn() // this === a
a 調(diào)用了fn() 所以此時(shí)this為a
a.fn.call ({name : "B"}) // this === {name : "B"}
使用call(),將this的值指定為{name:"B"}
var fn1 = a.fn
fn1() // this === window
雖然指定fn1 = a.fn歼捐,但是調(diào)用是有window調(diào)用,所以this 為window
this 有多種使用場(chǎng)景:
- 作為構(gòu)造函數(shù)執(zhí)行:
function Student(name,age) {
this.name = name // this === s
this.age = age // this === s
//return this
}
var s = new Student("py1988",30)
- 作為普通函數(shù)執(zhí)行:
function fn () {
console.log (this) // this === window
}
fn ();
- 作為對(duì)象屬性執(zhí)行:
var obj = {
name : "A",
printName : function () {
console.log (this.name) // this === obj
}
}
obj.printName ()
4.call(), apply(), bind(): 這是重點(diǎn)晨汹!
三個(gè)函數(shù)都可以修改this的指向:
var name = "小明" , age = "17"
var obj = {
name : "安妮",
objAge :this.age,
fun : function (like,dislike) {
console.log (this.name + "今年" + this.age 豹储,"喜歡吃" + like + "不喜歡吃" + dislike) ;
}
}
var a = { name : "Jay", age : 23 }
obj.fun.call(a,"蘋果","香蕉") // Jay今年23 喜歡吃蘋果不喜歡吃香蕉
obj.fun.apply(a,["蘋果","香蕉"]) // Jay今年23 喜歡吃蘋果不喜歡吃香蕉
obj.fun.bind(a,"蘋果","香蕉")() // Jay今年23 喜歡吃蘋果不喜歡吃香蕉
首先 call,apply淘这,bind 第一個(gè)參數(shù)都是 this 指向的對(duì)象剥扣,call 和 apply 如果第一個(gè)參數(shù)指向 null 或 undefined 時(shí),那么 this 會(huì)指向 windows 對(duì)象铝穷。
call钠怯,apply,bind 的執(zhí)行方式如上例所示曙聂。call晦炊,apply 都是改變上下文中的 this,并且是立即執(zhí)行的宁脊。bind 方法可以讓對(duì)應(yīng)的函數(shù)想什么時(shí)候調(diào)用就什么時(shí)候調(diào)用断国。
閉包:
閉包的概念很抽象,看下面的例子你就會(huì)理解什么叫閉包了:
function a(){
var n = 0;
this.fun = function () {
n++;
console.log(n);
};
}
var c = new a();
c.fun(); //1
c.fun(); //2
閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)榆苞。在 js 中只有函數(shù)內(nèi)部的子函數(shù)才能讀取局部變量稳衬。所以可以簡(jiǎn)單的理解為:定義在內(nèi)部函數(shù)的函數(shù)。
異步和單線程
我們先感受下異步坐漏。
console.log("start");
setTimeout(function () {
console.log("medium");
}, 1000);
console.log("end");
使用異步后薄疚,打印的順序?yàn)?start-> end->medium。因?yàn)闆](méi)有阻塞赊琳。
為什么會(huì)產(chǎn)生異步呢街夭?
首先因?yàn)?js 為單線程,也就是說(shuō) CPU 同一時(shí)間只能處理一個(gè)事務(wù)躏筏。得按順序莱坎,一個(gè)一個(gè)處理。
如上例所示寸士,第一步:執(zhí)行第一行打印 “start”檐什;第二步:執(zhí)行 setTimeout碴卧,將其中的函數(shù)分存起來(lái),等待時(shí)間結(jié)束后執(zhí)行乃正;第三步:執(zhí)行最后一行住册,打印 “end”;第四部:處于空閑狀態(tài)瓮具,查看暫存中荧飞,是否有可執(zhí)行的函數(shù);第五步:執(zhí)行分存函數(shù)名党。
為什么 js 引擎是單線程叹阔?
js 的主要用途是與用戶互動(dòng),以及操作 DOM传睹,這決定它只能是單線程耳幢。例:一個(gè)線程要添加 DOM 節(jié)點(diǎn),一個(gè)線程要?jiǎng)h減 DOM 節(jié)點(diǎn)欧啤,容易造成分歧睛藻。
為了更好使用多 CPU,H5 提供了 web Worker 標(biāo)準(zhǔn)邢隧,允許 js 創(chuàng)建多線程店印,但是子線程受到主線程控制,而且不得操作 DOM倒慧。
任務(wù)列隊(duì)
單線程就意味著按摘,所有的任務(wù)都要排隊(duì),前一個(gè)結(jié)束纫谅,才會(huì)執(zhí)行后面的任務(wù)院峡。如果列隊(duì)是因?yàn)橛?jì)算量大,CPU 忙不過(guò)來(lái)系宜,倒也算了照激。但是更多的時(shí)候,CPU 是閑置的盹牧,因?yàn)?IO 設(shè)備處理得很慢俩垃,例如 ajax 讀取網(wǎng)絡(luò)數(shù)據(jù)。js 設(shè)計(jì)者便想到汰寓,主線程完全可以不管 IO 設(shè)備口柳,將其掛起,然后執(zhí)行后面的任務(wù)有滑。等后面的任務(wù)結(jié)束掉跃闹,在反過(guò)頭來(lái)處理掛起的任務(wù)。
好,我們來(lái)梳理一下:
1)所有的同步任務(wù)都在主線程上執(zhí)行望艺,行程一個(gè)執(zhí)行棧苛秕。
2)除了主線程之外,還存在一個(gè)任務(wù)列隊(duì)找默,只要一步任務(wù)有了運(yùn)行結(jié)果艇劫,就在任務(wù)列隊(duì)中植入一個(gè)時(shí)間。
3)主線程完成所有任務(wù)惩激,就會(huì)讀取列隊(duì)任務(wù)店煞,并將其執(zhí)行。
4)重復(fù)上面三步风钻。
只要主線程空了顷蟀,就會(huì)讀取任務(wù)列隊(duì),這就是 js 的運(yùn)行機(jī)制骡技,也被稱為 event loop(事件循環(huán))鸣个。
ES6 語(yǔ)法
ES6,強(qiáng)制開(kāi)啟嚴(yán)格模式!
let 和 const
作用域的概念:
全局作用域哮兰,函數(shù)作用域毛萌,塊作用域
<暫時(shí)性死區(qū)>
let和const不能重復(fù)定義苟弛;const聲明時(shí)必須賦值喝滞,let 不必須;
解構(gòu)賦值
解構(gòu)賦值的分類:
數(shù)組解構(gòu)賦值膏秫,對(duì)象解構(gòu)賦值右遭,字符串解構(gòu)賦值,
布爾值解構(gòu)賦值缤削,函數(shù)參數(shù)解構(gòu)賦值窘哈,數(shù)值解構(gòu)賦值
使用場(chǎng)景:
數(shù)組結(jié)構(gòu)賦值:
(1)默認(rèn)值:
function fn(a, b=2){
...
}
(2)變量交換
{
let a = 1;
let b = 2;
[a, b] = [b, a];
console.log(a, b);
}
(3)獲取函數(shù)返回值
{
function f(){
return [1,2,3,4];
}
let a, b, c, d;
[a, b,c亭敢,d] = f(); // 接收所有值
[a, , , b] = f(); // 接收指定值
[a, ...b] = f(); // 分開(kāi)接收
}
對(duì)象解構(gòu)賦值:
{
let o = {p:42, q:true}
let {p,q} = o;
console.log(p,q);
}
(1)默認(rèn)值:
{
let {a = 5, b = 10} = {a : 3}
console.log(a,b) // 3, 5
}
(2)json對(duì)象取值 :特別常用的場(chǎng)景滚婉!
{
let data = {
title: 'abc',
test: [{
title: 'test',
desc: '描述'
}]
}
// 聲明一個(gè)跟解構(gòu)對(duì)象格式一致的對(duì)象,來(lái)接收對(duì)象中的值
let {title : esTitle, test: [{title: cnTitle}]} = data;
console.log(esTitle, cnTitle);
}
正則擴(kuò)展:
構(gòu)造函數(shù)的變化帅刀、正則方法擴(kuò)展忙灼、u修飾符藤韵、y修飾符、s修飾符
es5中有一下兩種正則表達(dá)式寫法:
{
// 兩個(gè)參數(shù)的情況,前面是匹配字符串
let regex = new RegExp('xyz', 'i');
// 一個(gè)參數(shù)的情況淮阐,前面是正則表達(dá)式
let regex2 = new RegExp(/xyz/i);
}
es6中,允許前面是正則表達(dá)式躺苦,而后面跟著修飾符的兩個(gè)參數(shù)的寫法:
{
let regex3 = new RegExp(/xyz/ig, 'i'); // 但這種寫法后面的修飾符會(huì)覆蓋正則中的修飾符
// flags是es6中正則表達(dá)式的新的API源葫,輸出的結(jié)果是正則表達(dá)式的修飾符
console.log(regex3.flags); // 輸出的是 ' i '
}
y修飾符:
{
let s = 'bbb_bb_b';
let a1 = /b+/g;
let a2 = /b+/y;
console.log('one', a1.exec(s), a2.exec(s));
console.log('two', a1.exec(s), a2.exec(s));
}
g和y修飾符的共同點(diǎn):都是全局匹配
不同點(diǎn):
g:可以不連續(xù)匹配,bbb_bb_b
y: 粘連模式,必須連續(xù)匹配b后面如果不是b了嘲恍,就不會(huì)繼續(xù)匹配
sticky可以查看是否開(kāi)啟了粘連模式:a1.sticky
u修飾符:
u修飾符足画,可以識(shí)別大于2個(gè)字節(jié)的字符;
es5中蛔钙,. 能匹配任何字符锌云,但是前提是小于兩個(gè)字節(jié)的
s修飾符:es6中還未實(shí)現(xiàn)
字符串?dāng)U展:(要安裝babel-polyfill)
處理unicode編碼大于0xffff的字符 => 用{}包裹起來(lái)
`\u20BB7` => `\u{20BB7}`
es5中:
獲取unicode編碼對(duì)應(yīng)的字符:fromCharCode()
;
獲取字符對(duì)應(yīng)的unicode編碼:charCodeAt()
;
es6中:
獲取unicode編碼對(duì)應(yīng)的字符:fromCharPoint()
;
獲取字符對(duì)應(yīng)的unicode編碼:codePointAt()
; // 能獲取超過(guò)0xFFFF長(zhǎng)度的編碼吁脱,并正確顯示
es6中有一個(gè)字符串遍歷器接口桑涎,可以正確處理unicode編碼大于0xFFFF的字符串
{
let str = '/u{20BB7}abc';
for(let code of str){
console.log('es6', code);
}
//let of 可以正確遍歷出0xFFFF編碼長(zhǎng)度的字符串對(duì)應(yīng)的字符,不會(huì)出現(xiàn)亂碼
}
str.includes('x')
:判斷字符串中是否包含了某個(gè)字符
str.startsWith('x')
:判斷字符串是不是已某個(gè)字符開(kāi)始的
str.endsWith('x')
:判斷字符串是不是已某個(gè)字符結(jié)束的
str.repeat(2)
:重復(fù)字符串
模板字符串(很重要):
`你好兼贡,${name}攻冷,歡迎你!`
padStart 和 padEnd (這兩個(gè)是ES7的草案遍希,需要babel-polyfill)
console.log('1'.padStart(2, '0')); // '01'
console.log('1'.padEnd(2, '0')); // '10'
標(biāo)簽?zāi)0澹?/h4>
1.怎么用等曼?
{
let user = {
name: 'list',
info: 'hello world'
};
console.log(abc`i am ${user.name}, ${user.info}`);
function abc(s, v1, v2){
console.log(s, v1, v2);
return s+v1+v2;
}
}
2.在哪里用?
防止XSS攻擊凿蒜,和處理多語(yǔ)言轉(zhuǎn)換的時(shí)候用
string.raw:可以將所有的 ' \ ' 進(jìn)行了轉(zhuǎn)義禁谦,即在前面又加了一個(gè)
String.raw`Hi\n${1+2}` // Hi\n3
數(shù)值擴(kuò)展:
Number.isFinite()
:判斷一個(gè)值是否有盡
Number.isNaN()
:判斷一個(gè)數(shù)是不是NaN
Number.isInteger()
:判斷是不是整數(shù) 2.0也是true
Number.MAX_SAFE_INTEGER
// 最大安全數(shù)值
Number.MIN_SAFE_INTEGER
// 最小安全數(shù)值
Number.isSafeInteger()
// 判斷一個(gè)數(shù)字是不是在安全數(shù)的范圍內(nèi):
Math.trunc(4.1); //4
// 取小數(shù)的整數(shù)部分
Math.sign() // 結(jié)果只有-1, 0 废封,1州泊, NaN
// 判斷是正數(shù),負(fù)數(shù)漂洋,還是零
Math.cbrt(8); // 2
// 計(jì)算立方根
還有三角函數(shù)方法遥皂,對(duì)數(shù)方法,都是es6新增的
數(shù)組擴(kuò)展:
Array.from
Array.of
copyWithin
find \ findIndex
fill
entries \ keys \ values
includes
函數(shù)擴(kuò)展:
1刽漂、參數(shù)默認(rèn)值
{
function test(x, y = " 23 "){
console.log(x, y);
}
test(2) // 2 23
}
// 要注意默認(rèn)值后面不能再有沒(méi)有默認(rèn)值的參數(shù)變量
// 這就是錯(cuò)誤的
function test(x, y = " 23 " , c) ...
關(guān)于取值作用域的問(wèn)題:
{
let x = 'test';
function test(x, y=x){
console.log(x,y);
}
test('kill'); // kill kill
}
2演训、rest參數(shù)
把一系列的參數(shù)都轉(zhuǎn)換為數(shù)組,在你不確定有多少個(gè)參數(shù)的時(shí)候
注意贝咙,rest參數(shù)后面不能再有其他的參數(shù)
{
function test( ...arg ){
for (let v of arg){
console.log(v);
}
}
test(1,2,3,4); // 1 2 3 4
}
3样悟、擴(kuò)展運(yùn)算符
擴(kuò)展運(yùn)算符和rest運(yùn)算符是一個(gè)相反的作用
它的作用是:把一個(gè)數(shù)組拆成離散的值
{
console.log(...[1,2,3]); // 1 2 3
console.log('a', ...[1,2,3]) // 'a' 1 2 3
}
4、箭頭函數(shù)
{
let arrow = v => v*2;
// 等同于
function arrow(v){
return v*2;
}
// 無(wú)參數(shù)
let arrow = () => v*3;
// 等同于
function arrow(){
return v*3;
}
}
5庭猩、this綁定
因?yàn)閠his在指向在箭頭函數(shù)和普通函數(shù)不一樣窟她,所以要考慮什么時(shí)候適合用箭頭函數(shù),什么時(shí)候不適合用箭頭函數(shù)眯娱;
this在普通函數(shù)中礁苗,指向函數(shù)被調(diào)用時(shí)的對(duì)象所在;
this在箭頭函數(shù)中徙缴,指向函數(shù)被定義時(shí)的對(duì)象所在试伙;
6嘁信、尾調(diào)用
區(qū)分特征:函數(shù)的最后一句話是不是函數(shù)
{
function tail(x){
console.log('tail', x);
}
function fx(x){
return tail(x);
}
fx(123);
}
// 尾調(diào)用的用處:提升(遞歸函數(shù),嵌套函數(shù))性能
對(duì)象(object)擴(kuò)展:
1疏叨、簡(jiǎn)介表示法:
{
let o = 1;
let k = 2;
// es5中聲明一個(gè)對(duì)象
let es5 = {
o: o,
k: k
};
// es6中潘靖,如果鍵值相同,可以這樣寫:
let es6 = {
o,
k
};
// 如果對(duì)象里面有方法:
let es5_method = {
hello: function(){}
};
let es6_method = {
hello(){}
}
}
2蚤蔓、屬性表達(dá)式:
{
let a = 'b';
let es5_obj = {
a: 'c',
b: 'c'
};
// 對(duì)象的key值卦溢,可以是一個(gè)表達(dá)式,這叫做屬性表達(dá)式
// 這里的 [a] = 'b';
let es6_obj = {
[a]: 'c'
}
console.log(es5_obj, es6_obj);
}
3秀又、擴(kuò)展運(yùn)算符:
babel對(duì)它的支持不是很好,加了babel-polyfill還是會(huì)報(bào)錯(cuò)
{
let {a, b, ...c} = {a: 'test', b:'re', c:'ccc', d:'ddd'};
解析過(guò)后c的值應(yīng)該是:
c: {
c:'ccc',
d:'ddd'
}
}
4单寂、Object新增方法:
// 新增API
Object.is() 的功能和 ‘===’ 沒(méi)有區(qū)別,判斷兩個(gè)值是否相等
Object.is(str, str); //判斷兩個(gè)字符串是否相等
// 要注意吐辙,數(shù)組和對(duì)象是引用類型宣决,雖然都是空數(shù)組,但是引用的地址不同昏苏,所以:
object.is([],[]) // 結(jié)果是false
Object.assign() : 淺拷貝
// 拷貝的是引用地址尊沸,不是值;
// 只拷貝自身的屬性贤惯,不會(huì)拷貝繼承的屬性洼专,和不可枚舉的屬性
Object.assign({a:''a}, {b:'b'}); // {a: 'a', b: 'b'}
Object.entries() : 方法返回一個(gè)給定對(duì)象自身可枚舉屬性的鍵值對(duì)數(shù)組,
其排列與使用 for...in 循環(huán)遍歷該對(duì)象時(shí)返回的順序一致
區(qū)別在于 for-in 循環(huán)也枚舉原型鏈中的屬性
const object1 = { foo: 'bar', baz: 42 };
console.log(Object.entries(object1)[1]);
// expected output: Array ["baz", 42]
const object2 = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.entries(object2)[2]);
// expected output: Array ["2", "c"]
const result = Object.entries(object2).sort((a, b) => a - b);
console.log(Object.entries(result)[1]);
// expected output: Array ["1", Array ["1", "b"]]
Symbol:
1孵构、Symbol的概念:
Symbol可提供一個(gè)獨(dú)一無(wú)二的值
let a1 = Symbol();
let a2 = Symbol();
console.log(a1 === a2); // false
// 定義了以后屁商,將來(lái)要取回來(lái),就用這種方式
let a3 = Symbol.for('a3'); // for中的a3是一個(gè)key值浦译,如果在全局注冊(cè)過(guò)棒假,就返回這個(gè)值溯职,如果沒(méi)有注冊(cè)過(guò)精盅,就注冊(cè)一個(gè)獨(dú)一無(wú)二的值
let a4 = Symbol.for('a3');
let a5 = Symbol.for('a3');
console.log(a4 === a5); // true
2、Symbol的作用:
{
let a1 = Symbol.for('abc');
let obj = {
[a1] : '123',
'abc' : 234,
'c' : 456
}
console.log('obj', obj); // {Symbol(abc): '123', 'abc': 234, 'c': 456}
要注意一點(diǎn):用Symbol做key值谜酒,通過(guò)for...in
和let...of
是拿不到key值的
for(let [key, value] of Object.entries(obj)){
console.log(key, value) ; //拿不到Symbol(abc)和其值
}
這時(shí)可以通過(guò)Object.getOwnPropertySymbols()
拿到Symbol定義的值,但是它拿不到其他的值
Object.getOwnPropertySymbols(obj).forEach(function(item){
console.log(obj[item]); // 123
})
如果想拿到所有的值叹俏,可以通過(guò)Reflect.ownKeys()
拿到
Reflect.ownKeys(obj).forEach(function(item){
console.log('ownKeys', item, obj[item])
})
ES6數(shù)據(jù)結(jié)構(gòu):
Set:
1、類似數(shù)組僻族,但集合中的元素不能重復(fù)
{
let list = new Set();
list.add(5); // 添加
list.add(7);
list.size // 獲取set中的長(zhǎng)度(個(gè)數(shù))
// 初始化set中的值
let arr = [1,2,3,4,5];
let list1 = new Set(arr);
// 不可重復(fù)性
let list2 = new Set();
list2.add(1);
list2.add(2);
list2.add(1);
console.log(list2); // 不會(huì)打印出后面重復(fù)的元素粘驰,也不會(huì)報(bào)錯(cuò),這可以用于去重
這里要注意述么,Set不會(huì)做數(shù)據(jù)類型的轉(zhuǎn)換蝌数,即 2 和 '2' 在set看來(lái)是不一樣的
2、Set實(shí)例的幾個(gè)方法和屬性值:
add()
:添加某個(gè)元素
delete()
:刪除某個(gè)元素
clear()
:清空所有元素
has()
:判斷有沒(méi)有某個(gè)元素度秘,返回布爾類型
size
:返回集合中元素的個(gè)數(shù)
3顶伞、Set實(shí)例的遍歷:
// for...of
for(let key of list.keys()){
console.log('keys', key);
}
for(let value of list.values()){
console.log('value', value);
}
for(let value of list){
console.log('value', value);
}
for(let [key, value] of list.entries()){
console.log('entries', key, value);
}
key和value的值一樣,都是元素的名稱;
不加.key或者.values唆貌,也是可以的
Map
1 滑潘、類似Object,key可以是任意類型(object中key只能是字符串)
{
let map = new Map();
let arr = ['123'];
map.set(arr, 456); // Map可以用數(shù)組作為key(或其他任意類型)哦~
console.log('map', map, map.get(arr));
// 結(jié)果:map Map{["123"] => 456} 456
// 初始化Map中的值
let map1 = new Map([['a', 123], ['b', 456]]); // 一定要注意這種格式
}
2锨咙、Map實(shí)例的幾個(gè)方法和屬性值:
get()
:獲取某個(gè)元素
set()
:添加某個(gè)元素
delete()
:刪除某個(gè)元素
clear()
:清空所有元素
has()
:判斷有沒(méi)有某個(gè)元素语卤,返回布爾類型
size
:返回集合中元素的個(gè)數(shù)
3、Map 結(jié)構(gòu)轉(zhuǎn)為數(shù)組結(jié)構(gòu)
比較快速的方法是結(jié)合使用擴(kuò)展運(yùn)算符(...)
let map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
[...map.keys()]
// [1, 2, 3]
[...map.values()]
// ['one', 'two', 'three']
[...map.entries()]
// [[1,'one'], [2, 'two'], [3, 'three']]
[...map]
// [[1,'one'], [2, 'two'], [3, 'three']]
4酪刀、Map 循環(huán)遍歷
Map 原生提供三個(gè)遍歷器:
keys()
:返回鍵名的遍歷器粹舵。
values()
:返回鍵值的遍歷器。
entries()
:返回所有成員的遍歷器骂倘。
let map = new Map([
['F', 'no'],
['T', 'yes'],
]);
for (let key of map.keys()) {
console.log(key);
}
// "F"
// "T"
for (let value of map.values()) {
console.log(value);
}
// "no"
// "yes"
for (let item of map.entries()) {
console.log(item[0], item[1]);
}
// "F" "no"
// "T" "yes"
// 或者
for (let [key, value] of map.entries()) {
console.log(key, value);
}
// 等同于使用map.entries()
// 這個(gè)例子可以看出:Map 結(jié)構(gòu)的默認(rèn)遍歷器接口(Symbol.iterator 屬性)齐婴,就是 entries 方法。
for (let [key, value] of map) {
console.log(key, value);
}
5稠茂、Map 獲取第一個(gè)元素
const m = new Map();
m.set('key1', {})
m.set('keyN', {})
console.log(m.entries().next().value); // [ 'key1', {} ]
// 獲取第一個(gè)key
console.log(m.keys().next().value); // key1
// 獲取第一個(gè)value
console.log(m.values().next().value); // {}
Map與Array的對(duì)比 柠偶、 Set與Array的對(duì)比
數(shù)據(jù)結(jié)構(gòu)橫向?qū)Ρ龋霾枪亍⒉橛盏!⒏摹h
// Map與Array
let map = new Map();
let array = [];
// 增
map.set('t', 1);
array.push({t:1});
console.info('may-array', map, array);
// 查
let map_exist = map.has('t');
let array_exist = array.find(item=>item.t);
console.info('may-array', map_exist, array_exist); //map返回的是布爾值电爹,數(shù)組返回的是這個(gè)值
// 改
map.set('t', 2);
array.forEach(item => item.t ? item.t = 2 : ' '); // 先要判斷是否存在蔫仙,才執(zhí)行
console.info('may-array-modify', map, array);
// 刪
map.delete('t');
let index = array.findIndex(item => item.t); // 查找每一個(gè)元素中帶t的元素
array.splice(index, 1);
console.info('may-array-del', map, array);
// Set與Array
let map = new Set();
let array = [];
// 增
set.add({t :1});
array.push({t:1});
console.info('set-array', set, array);
// 查
let set_exist = set.has({t:1}); //這里應(yīng)該是填入保存的地址,這樣寫會(huì)報(bào)錯(cuò)
let array_exist = array.find(item=>item.t);
console.info('may-array', set_exist, array_exist); //set返回的是布爾值丐箩,數(shù)組返回的是這個(gè)值
// 改
set.forEach(item => item.t ? item.t = 2: ' ');
array.forEach(item => item.t ? item.t = 2 : ' '); // 先要判斷是否存在摇邦,才執(zhí)行
console.info('may-array-modify', set, array);
// 刪
set.forEach(item => item.t ? set.delete(item) : ' ');
let index = array.findIndex(item => item.t); // 查找每一個(gè)元素中帶t的元素
array.splice(index, 1);
console.info('may-array-del', set, array);
Proxy和Reflect
Proxy:可以用來(lái)代理對(duì)對(duì)象的訪問(wèn)
Reflect 的方法和用法和Proxy一模一樣。只是使用方式不一樣屎勘,Proxy需要new施籍,而Reflect直接使用(Reflect.get())
// Proxy的幾個(gè)常用方法:
// 其中target為代理對(duì)象參數(shù),原本obj的屬性概漱,它都有丑慎,實(shí)際傳入的代理對(duì)象,是proxy的實(shí)例化對(duì)象
// 獲取對(duì)象屬性
get(target, key){}
// 設(shè)置對(duì)象屬性
set(target, key, value){}
// 判斷對(duì)象是否有某個(gè)屬性,可以攔截判斷瓤摧,欺騙訪問(wèn)
has(target, key){}
// 刪除對(duì)象屬性:攔截delete竿裂,如果不想用戶刪除對(duì)象中的某些屬性
deleteProperty(target, key){}
// 攔截Object.keys, Object.getOwnPropertySymbols, Object.getOwnPropertyNames
ownKeys(){}
完整示例,及Proxy在開(kāi)發(fā)中的使用:
{
let obj = {
time: '2017-03-11',
name: 'net',
_r: 123
}
let monitor = new Proxy(obj, {
// 攔截對(duì)象屬性的讀取
get(target, key) {
return target[key].replace('2017', '2018')
},
// 攔截對(duì)象設(shè)置屬性
set(target, key, value) {
if (key === 'name') {
return (target[key] = value)
} else {
return target[key]
}
},
// 攔截key in object操作
has(target, key) {
if (key === 'name') {
return target[key]
} else {
return false
}
},
// 攔截delete
deleteProperty(target, key) {
if (key.indexOf('_') > -1) {
delete target[key]
return true
} else {
return target[key]
}
},
// 攔截Object.keys,Object.getOwnPropertySymbols,Object.getOwnPropertyNames
ownKeys(target) {
return Object.keys(target).filter(item => item != 'time')
}
})
console.log('get', monitor.time)
monitor.time = '2018'
monitor.name = 'mukewang'
console.log('set', monitor.time, monitor)
console.log('has', 'name' in monitor, 'time' in monitor)
// delete monitor.time;
// console.log('delete',monitor);
//
// delete monitor._r;
// console.log('delete',monitor);
console.log('ownKeys', Object.keys(monitor))
}
{
let obj = {
time: '2017-03-11',
name: 'net',
_r: 123
}
console.log('Reflect get', Reflect.get(obj, 'time'))
Reflect.set(obj, 'name', 'mukewang')
console.log(obj)
console.log('has', Reflect.has(obj, 'name'))
}
// proxy常用的情景:對(duì)數(shù)據(jù)類型進(jìn)行校驗(yàn)照弥,實(shí)現(xiàn)與業(yè)務(wù)解耦
{
function validator(target, validator) {
return new Proxy(target, {
_validator: validator,
set(target, key, value, proxy) {
// 判斷是否有對(duì)應(yīng)可操作的屬性
if (target.hasOwnProperty(key)) {
let va = this._validator[key]
if (!!va(value)) {
// 如果所訪問(wèn)的屬性有值腻异,則執(zhí)行代理操作(這里僅以Set為例),將代理返回到真實(shí)對(duì)象proxy
return Reflect.set(target, key, value, proxy)
} else {
throw Error(`不能設(shè)置${key}到${value}`)
}
} else {
// 如果沒(méi)有这揣,則拋出異常
throw Error(`${key} 不存在`)
}
}
})
}
// 模擬一個(gè)條件類
const personValidators = {
name(val) {
return typeof val === 'string'
},
age(val) {
return typeof val === 'number' && val > 18
},
mobile(val) {}
}
// 模擬一個(gè)被代理的類
class Person {
constructor(name, age) {
this.name = name
this.age = age
this.mobile = '1111'
// 這里的this悔常,指的是Person的實(shí)例化對(duì)象
// Person類返回一個(gè)validator方法敢会,validator方法返回一個(gè)proxy代理,
//也就是說(shuō)訪問(wèn)Person的實(shí)例这嚣,會(huì)經(jīng)過(guò)validator中的proxy代理鸥昏,進(jìn)行篩選過(guò)濾
return validator(this, personValidators)
}
}
// 實(shí)例化Person類
const person = new Person('lilei', 30)
console.info(person)
person.name = 'Han mei mei'
console.info(person)
}
ES6中的 類和對(duì)象
類
基本語(yǔ)法 | 類的繼承 | 靜態(tài)方法 | 靜態(tài)屬性 | getter | setter
1、基本定義和生成實(shí)例
class Parent{
// 定義類中的構(gòu)造函數(shù)
constructor(name = 'zk'){
this.name = name;
}
}
let v_parent = new Parent('z');
console.log('構(gòu)造函數(shù)實(shí)例', v_parent)
2姐帚、類的繼承
定義一個(gè)父類吏垮,子類來(lái)繼承
class parent{
constructor(name='zk'){
this.name = name;
}
}
class child extends parent{
// 繼承父類的子類,可以覆蓋 / 把實(shí)際需要的參數(shù)傳遞給 父類的參數(shù)
constructor(name = 'child'){
super(name); //要覆蓋父類哪個(gè)參數(shù)罐旗,就傳遞哪個(gè)參數(shù)名膳汪;如果不傳,則默認(rèn)全部使用父類參數(shù)的默認(rèn)值
// 如果子類還要增加自己的屬性九秀,一定要放在super之后
this.type = 'child';
}
}
console.log('繼承', new Child('hello'));
getter 和 setter
class Parent{
constructor(name = 'zk'){
this.name = name;
}
// getter
// 注意遗嗽,get后面的是屬性,不是方法鼓蜒!
get longName(){
return 'mk' + this.name
}
// setter
set longName(value){
this.name = value
}
}
// get操作
let v = new Parent();
console.log('getter',v.longName);
// set操作
v.longName = 'hello'
console.log('setter',v.longName);
靜態(tài)方法 的定義和使用
// static只是用來(lái)定義靜態(tài)方法痹换,不是用來(lái)定義靜態(tài)屬性用的!
// 靜態(tài)屬性是直接在類上定義的都弹。
class Parent{
constructor(name = 'zk'){
this.name = name;
}
static tell(){
console.log('tell')
}
}
Parent.tell(); // tell
靜態(tài)屬性的定義和使用(靜態(tài)屬性沒(méi)有關(guān)鍵詞(暫時(shí)沒(méi)有娇豫,雖然有提案))
class Parent{
constructor(name = 'zk'){
this.name = name;
}
static tell(){
console.log('tell')
}
}
Parent.type = 'type'; // 直接在類上定義屬性,不用new實(shí)例
console.log('靜態(tài)屬性', Parent.type);
思考:什么樣的場(chǎng)景下適合用靜態(tài)方法畅厢,什么樣的場(chǎng)景下適合用靜態(tài)屬性?
Promise
什么是異步 | Promise的作用 | Promise的基本用法
現(xiàn)在的js異步編程的方式有一下幾種:
1.回調(diào)函數(shù) 2.事件監(jiān)聽(tīng) 3.發(fā)布/訂閱 4.Promise 對(duì)象 5.Generator
之前有兩種方式:通過(guò)回調(diào)函數(shù)冯痢、通過(guò)觸發(fā)方法
而現(xiàn)在的Promise更適合異步操作
// 通過(guò)模擬ajax請(qǐng)求,來(lái)使用promise實(shí)現(xiàn)異步操作
{
let ajax = function(){
console.log('執(zhí)行1');
return new Promise(function(resolve, reject){
setTimeout(function(){
resolve()
}, 1000)
})
}
ajax().then(function(){
console.log("然后執(zhí)行2");
})
}
如果是多個(gè)異步操作呢框杜?
// 在then()中繼續(xù)使用promise即可
{
let ajax=function(){
console.log('執(zhí)行3');
return new Promise(function(resolve, reject){
setTimeout(function () {
resolve()
}, 1000);
})
};
ajax().then(function(){
return new Promise(function(resolve, reject){
setTimeout(function () {
resolve()
}, 2000);
});
}).then(function(){
console.log('timeout3');
})
}
Promise 提供 then 方法加載回調(diào)函數(shù)浦楣,catch方法捕捉執(zhí)行過(guò)程中拋出的錯(cuò)誤。catch捕獲出錯(cuò)信息咪辱,err就是具體的出錯(cuò)信息
{
let ajax=function(num){
console.log('執(zhí)行4');
return new Promise(function(resolve,reject){
if(num>5){
resolve()
}else{
throw new Error('出錯(cuò)了')
}
})
}
ajax(6).then(function(){
console.log('log',6);
}).catch(function(err){
console.log('catch',err);
});
ajax(3).then(function(){
console.log('log',3);
}).catch(function(err){
console.log('catch',err);
});
}
promise的高級(jí)用法
promise.all promise.race(賽跑)
情景1:保證所有圖片都加載完成振劳,再顯示頁(yè)面
{
// 所有圖片加載完再添加到頁(yè)面
function loadImg(src){
return new Promise((resolve,reject)=>{
let img=document.createElement('img');
img.src=src;
img.onload=function(){
resolve(img);
}
img.onerror=function(err){
reject(err);
}
})
}
function showImgs(imgs){
imgs.forEach(function(img){
document.body.appendChild(img);
})
}
Promise.all([
loadImg('http://i4.buimg.com/567571/df1ef0720bea6832.png'),
loadImg('http://i4.buimg.com/567751/2b07ee25b08930ba.png'),
loadImg('http://i2.muimg.com/567751/5eb8190d6b2a1c9c.png')
]).then(showImgs)
}
情景2:同一個(gè)資源有多個(gè)來(lái)源,只要一個(gè)來(lái)源請(qǐng)求成功梧乘,就添加到頁(yè)面澎迎,其他的忽略不管(先到先得)
{
// 有一個(gè)圖片加載完就添加到頁(yè)面
function loadImg(src){
return new Promise((resolve,reject)=>{
let img=document.createElement('img');
img.src=src;
img.onload=function(){
resolve(img);
}
img.onerror=function(err){
reject(err);
}
})
}
function showImgs(img){
let p=document.createElement('p');
p.appendChild(img);
document.body.appendChild(p)
}
Promise.race([
loadImg('http://i4.buimg.com/567571/df1ef0720bea6832.png'),
loadImg('http://i4.buimg.com/567751/2b07ee25b08930ba.png'),
loadImg('http://i2.muimg.com/567751/5eb8190d6b2a1c9c.png')
]).then(showImgs)
}
Iterator 和 for...of 循環(huán)
什么是Iterator接口 | Iterator的基本用法 | for...of 循環(huán)
for...of只能遍歷數(shù)組庐杨,如果其他數(shù)據(jù)格式也想通過(guò)for...of來(lái)遍歷选调,前提條件是它本身自帶Iterator接口,如果沒(méi)有灵份,則需要為其自定義Iterator接口
數(shù)組支持遍歷仁堪,因?yàn)樗旧硪呀?jīng)封裝好了Iterator接口:
// arr本身有一個(gè)Iterator的方法,通過(guò)arr[Symbol.iterator]()可以觸發(fā)填渠,這個(gè)方法會(huì)返回一個(gè)對(duì)象弦聂,對(duì)象有一個(gè)next()方法鸟辅,用來(lái)輸出每次遍歷的結(jié)果
{
let arr=['hello','world'];
let map=arr[Symbol.iterator]();
console.log(map.next());
console.log(map.next());
console.log(map.next());
結(jié)果:
Object{value: 'hello', done: false}
Object{value: 'world', done: false}
Object{value: undefined, done: true}
}
如果其他沒(méi)有Iterator接口的數(shù)據(jù)類型,也想實(shí)現(xiàn)遍歷莺葫,則可以自定義Iterator接口
{
let obj={
start:[1,3,2],
end:[7,9,8],
[Symbol.iterator](){
let self=this;
let index=0;
let arr=self.start.concat(self.end);
let len=arr.length;
return {
// 注意:這里要返回一個(gè)對(duì)象匪凉,對(duì)象中包含next()方法,而且返回值有兩個(gè)捺檬,value和done
next(){
if(index<len){
return {
// 注意每遍歷一次再层,index都要+1
value:arr[index++],
done:false
}
}else{
return {
value:arr[index++],
done:true
}
}
}
}
}
}
// 本身for...of只能用來(lái)遍歷數(shù)組,但通過(guò)為obj自定義了Iterator接口堡纬,從而使for...of也可以遍歷obj
for(let key of obj){
console.log(key);
}
}
說(shuō)到for...of聂受,就來(lái)回顧一下js中,數(shù)據(jù)的操作方法 和 幾個(gè)循環(huán)的區(qū)別:
數(shù)組的操作方法:
會(huì)改變?cè)瓟?shù)組的有:pop push unshift shift splice reverse sort
不會(huì)改變?cè)瓟?shù)組的有:indexOf lastIndexof concat slice
其他常用操作方法:
es5的有:forEach filter map some every reduce
es6的有:find includes
幾個(gè)循環(huán)方法及其區(qū)別:
forEach烤镐、for蛋济、for...in、for..of
1炮叶、for (編程式)
let arr = [1,2,3,4,5];
for(let i = 0;i<arr.length;i++){
console.log(arr[i]);
}
2碗旅、forEach (聲明式:不關(guān)心如何實(shí)現(xiàn))
// 不支持return,它會(huì)遍歷到結(jié)束
let arr = [1,2,3,4,5];
arr.forEach(function(item){
console.log(item);
})
3镜悉、for...in (平時(shí)不用)
支持return扛芽,但一般不用它遍歷數(shù)組
1- 因?yàn)樗饕皇菙?shù)字,是字符串积瞒;
2- 因?yàn)閒or...in還會(huì)遍歷出數(shù)組的私有屬性川尖,會(huì)影響length的準(zhǔn)確性
let arr = [1,2,3,4,5];
arr.b = '100';
for(let key in arr){
// key會(huì)變成字符串類型
console.log(key) // 會(huì)把私有屬性b也遍歷出來(lái)
}
4、for...of (es6語(yǔ)法)
// 用于遍歷數(shù)組
// 既可以return茫孔,又不會(huì)遍歷出私有屬性
let arr = [1,2,3,4,5];
for(let value of arr){
console.log(value);
}
// 如果想讓for...of遍歷對(duì)象叮喳,不僅可以通過(guò)Iterator添加接口的方式,也可以通過(guò)Object.keys()來(lái)變通實(shí)現(xiàn)
let obj = {
school: 'zk',
age: 9
}
for(let value of Object.keys(obj)){
console.log(obj[value])
}
filter的用法:
是否操作原數(shù)組:不操作
返回結(jié)果:過(guò)濾后的新數(shù)組
回調(diào)函數(shù)返回結(jié)果:如果返回true缰贝,則將這一項(xiàng)放入新數(shù)組中
// 如果想篩選(刪除)某些項(xiàng)馍悟,可以用filter
{
let arr = [1,2,3,4,5];
let newArr = arr.filter(function(item){
return item>2 && item<5;
});
console.log(newArr); // [3,4]
}
map(映射)的用法:
將原有的數(shù)組映射成一個(gè)新的數(shù)組
是否操作原數(shù)組:不操作
返回結(jié)果:映射后的新數(shù)組
回調(diào)函數(shù)返回結(jié)果:回調(diào)函數(shù)中返回什么,這一項(xiàng)就是什么
// 可以用于將json返回值剩晴,拼接成dom元素锣咒,即更新某些項(xiàng),可以用map
{
let arr1 = [1,2,3];
let newArr1 = arr1.map(function(item){
return `<li>${item}</li>`
});
console.log(newArr1.join('')); // <li>1</li><li>2</li><li>3</li>
}
includes的用法:
看數(shù)組中是否包含某個(gè)值
是否操作原數(shù)組:不操作
返回結(jié)果:判斷結(jié)果布爾值
回調(diào)函數(shù)返回結(jié)果:包含則返回true赞弥,否則返回false
{
let arr3 = [1,2,3,4,5,6];
console.log(arr3.includes(5));
}
find的用法:
找到數(shù)組中的某個(gè)值
是否操作原數(shù)組:不操作
返回結(jié)果:判斷找到的值
回調(diào)函數(shù)返回結(jié)果:回調(diào)中返回true毅整,表示找到了,找到后即停止遍歷绽左;找不到返回undefined
{
let arr4 = [1,2,3,4,5,55,6];
let result = arr4.find(function(item, index){
return item.toString().indexOf(5) > -1
})
console.log(result);
}
some的用法:
找到true后停止悼嫉,返回true ;類似 “或”
是否操作原數(shù)組:不操作
返回結(jié)果:判斷數(shù)組中是否存在某值符合條件
回調(diào)函數(shù)返回結(jié)果:判斷有一個(gè)條件成立(或存在某值)拼窥,就返回true戏蔑,并立即停止
{
let arr4 = [1,2,3,4,5];
let result = arr4.some(function(item, index){
return item.toString().indexOf(5) > -1
})
console.log(result);
}
every的用法:
找到false后停止蹋凝,返回false蚣抗;類似 “與”
是否操作原數(shù)組:不操作
返回結(jié)果:判斷數(shù)組中是否所有的值都符合條件
回調(diào)函數(shù)返回結(jié)果:判斷一旦有一個(gè)條件不符合(或不存在某值)感凤,就返回false,并立即停止
{
let arr4 = [1,2,3,4,5,55,6];
let result = arr4.every(function(item, index){
return item.toString().indexOf(5) > -1
})
console.log(result);
}
reduce(翻譯:收斂闻葵,使還原情龄,使變?nèi)?
有四個(gè)參數(shù)(prev, next, index, item)
用法:將上一次的結(jié)果作為下一次的第一個(gè)伐割,繼續(xù)和后面的數(shù)據(jù)進(jìn)行操作
即1和2的結(jié)果,與3操作刃唤,他們的結(jié)果再與4操作隔心,以此類推
是否操作原數(shù)組:不操作
返回結(jié)果:返回疊加后的結(jié)果
回調(diào)函數(shù)返回結(jié)果:返回回調(diào)函數(shù)返回的結(jié)果
`
{
let arr4 = [1,2,3,4,5];
//第一次 prev:代表數(shù)組的前一項(xiàng);next:代表數(shù)組的第二項(xiàng)
//第二次 prev:是數(shù)組的上一次返回值尚胞;next:是數(shù)組的第三項(xiàng)
//如果沒(méi)有返回值硬霍,則第二次的prev便是undefined
let reduce = arr4.reduce(function(prev, next, index, item){
// item為原數(shù)組
console.log(arguments);
// return 100;
})
console.log(reduce );
}
應(yīng)用舉例:
1、計(jì)算多個(gè)商品的單價(jià)和數(shù)量的價(jià)格總和笼裳;
2唯卖、將二維數(shù)組,變成一維數(shù)組躬柬;
// 應(yīng)用1:
let sum2 = [{price:30,count:2},{price:30,count:3},{price:30,count:4} ].reduce(function(prev, next){
console.log(prev, next)
return prev+next.price*next.count
}, 0); // 這個(gè)0拜轨,是默認(rèn)執(zhí)行第一次的prev(即默認(rèn)第一項(xiàng)的值)
// reduce有兩個(gè)參數(shù):第一個(gè)是方法,第二個(gè)是默認(rèn)第一項(xiàng)
// 應(yīng)用2:
let arrs = [[1,2,3],[4,5,6],[7,8,9]];
let flat = arrs.reduce(function(prev,next){
return prev.concat(next);
});
console.log(flat); // [1,2,3,4,5,6,7,8,9]
Generator(發(fā)生器)
異步編程解決方案(相對(duì)promise更高級(jí))
基本概念 | next函數(shù)的用法 | yield*的用法
Generator是什么允青?
舉例來(lái)說(shuō)橄碾,讀取文件的協(xié)程寫法如下。
functionasnycJob(){
// ...其他代碼
var f=yield readFile( fileA);
// ...其他代碼
}
上面代碼的函數(shù) asyncJob 是一個(gè)協(xié)程颠锉,它的奧妙就在其中的 yield 命令法牲。它表示執(zhí)行到此處,執(zhí)行權(quán)將交給其他協(xié)程琼掠。也就是說(shuō)拒垃,yield命令是異步兩個(gè)階段的分界線。
協(xié)程遇到 yield 命令就暫停瓷蛙,等到執(zhí)行權(quán)返回悼瓮,再?gòu)臅和5牡胤嚼^續(xù)往后執(zhí)行。它的最大優(yōu)點(diǎn)艰猬,就是代碼的寫法非常像同步操作横堡,如果去除yield命令,簡(jiǎn)直一模一樣姥宝。
以上舉例轉(zhuǎn)載自:http://www.reibang.com/p/56ec5c113f1b翅萤,如要了解關(guān)于Generator,可參看腊满。
Generator 函數(shù)是協(xié)程在 ES6 的實(shí)現(xiàn)套么,最大特點(diǎn)就是可以交出函數(shù)的執(zhí)行權(quán)(即暫停執(zhí)行)。
Generator包含next和yield碳蛋,一旦步驟遇到y(tǒng)ield或return胚泌,就停止,遇到next就進(jìn)行下一步肃弟,直到結(jié)束
Generator函數(shù)定義:
// 需要babel-polyfill支持
{
// genertaor基本定義
let tell=function* (){
yield 'a';
yield 'b';
return 'c'
};
let k=tell();
console.log(k.next());
console.log(k.next());
console.log(k.next());
console.log(k.next());
}
為其他數(shù)據(jù)結(jié)構(gòu)部署Iterator接口玷室,實(shí)現(xiàn)遍歷,不光可以按照之前Iterator接口手動(dòng)實(shí)現(xiàn)的寫法笤受,還可以使用Generator穷缤。
Generator就是一個(gè)遍歷器生成接口
Generator 應(yīng)用,實(shí)現(xiàn)遍歷器接口(一種應(yīng)用情景)
{
let obj={};
obj[Symbol.iterator]=function* (){
yield 1;
yield 2;
yield 3;
}
for(let value of obj){
console.log('value',value);
}
}
Generator最大的優(yōu)勢(shì) : 狀態(tài)機(jī)
比如一個(gè)對(duì)象只有三種狀態(tài)箩兽,A,B,C 津肛,他們會(huì)在這三種狀態(tài)中循環(huán)出現(xiàn)。
{
let state = function* (){
// 因?yàn)橐恢倍际莟rue汗贫,所以會(huì)一直循環(huán)
while(1){
yield 'A';
yield 'B';
yield 'C';
}
}
let status = state();
console.log(status.next());// Object {value: 'A', done: false}
console.log(status.next());// Object {value: 'B', done: false}
console.log(status.next());// Object {value: 'C', done: false}
console.log(status.next());// Object {value: 'A', done: false}
console.log(status.next());// Object {value: 'B', done: false}
...
}
async...await 語(yǔ)法是Generator函數(shù)的一個(gè)語(yǔ)法糖
{
let state=async function (){
while(1){
await 'A';
await 'B';
await 'C';
}
}
let status=state();
console.log(status.next());
console.log(status.next());
console.log(status.next());
console.log(status.next());
console.log(status.next());
}
Generator的應(yīng)用:
1身坐、剩余抽獎(jiǎng)次數(shù)邏輯
{
let draw=function(count){
//具體抽獎(jiǎng)邏輯
console.info(`剩余${count}次`)
}
let residue=function* (count){
while (count>0) {
count--;
yield draw(count);
}
}
let star=residue(5);
let btn=document.createElement('button');
btn.id='start';
btn.textContent='抽獎(jiǎng)';
document.body.appendChild(btn);
document.getElementById('start').addEventListener('click',function(){
star.next();
},false)
}
2、長(zhǎng)輪巡
{
// 長(zhǎng)輪詢
let ajax=function* (){
yield new Promise(function(resolve,reject){
//模擬每200毫秒請(qǐng)求一次
setTimeout(function () {
resolve({code:0})
}, 200);
})
}
let pull=function(){
// generator實(shí)例
let genertaor=ajax();
let step=genertaor.next();
// step.value就是Promise實(shí)例
step.value.then(function(d){
if(d.code!=0){
setTimeout(function () {
console.info('wait');
pull()
}, 1000);
}else{
console.info(d);
}
})
}
pull();
}
Decorator(修飾器)[針對(duì)類]
基本概念 :
修飾器是一個(gè)函數(shù)落包,用來(lái)修改類的行為(或擴(kuò)展類的功能)
使用準(zhǔn)備:
1部蛇、除了babel-polyfill,還需要安裝一個(gè)插件包:
babel-plugin-transform-decorators-legacy
2咐蝇、修改.babelrc文件
{ "presets":["es2015"], "plugins":["transform-decorators-legacy"] }
3涯鲁、再重啟服務(wù)
基本用法:
聲明函數(shù)體:
{
// 聲明一個(gè)修飾器
let readonly = function(target, name, descriptor){
descriptor.writable = false; //描述是否可寫
return descriptor
}
}
使用(在類的里面):
{
class Test{
@readonly
time(){
return '2018-03-09'
}
}
}
let test = new Test();
// 嘗試修改類
test.time = function(){
console.log('reset time');
}
console.log(test.time());
//會(huì)報(bào)錯(cuò)提示無(wú)法修改只讀屬性time
Uncaught TypeError: Cannot assign to read only property 'time' of object '#<Test>'
在類的外面使用:
{
let typename=function(target,name,descriptor){
target.myname='hello'; // target指的就是類本身
}
@typename
class Test{
}
console.log('類修飾符',Test.myname);
// 第三方庫(kù)修飾器的js庫(kù):core-decorators;
// 可以通過(guò)npm安裝:npm install core-decorators
// import 進(jìn)來(lái)以后,直接就可以@使用
}
Decorator的應(yīng)用:
做埋點(diǎn)有序,日志統(tǒng)計(jì)
把埋點(diǎn)系統(tǒng)抽離出來(lái)撮竿,成為一個(gè)可復(fù)用的模塊
{
let log=(type)=>{
return function(target,name,descriptor){
let src_method=descriptor.value;
descriptor.value=(...arg)=>{
src_method.apply(target,arg);
console.info(`log ${type}`);
}
}
}
class AD{
@log('show')
show(){
console.info('ad is show')
}
@log('click')
click(){
console.info('ad is click');
}
}
let ad=new AD();
ad.show();
ad.click();
}