持续更新中... ...
数组合并
push apply
eg:
let arr1 = [1,2,3];
let arr2 = [4,5,6];
arr1.push.apply(arr1, arr2);
结果:
push and flat
eg:
let arr1 = [1,2,3];
let arr2 = [4,5,6];
arr1.push(arr2);
let _arr1 = arr1.flat(Infinity);
console.log(_arr1);
结果:
类型转换
split()
- 字符串转数组 eg:
// 字符串转数组
text = "aa bb cc";
console.log(text.split(/\s+/));
结果:
parseInt()
- 转化为number类型 eg:
// 强制转化为number类型
var num = '546';
console.log(parseInt(num));
结果:
toString()
- 转化为字符串类型 eg:
// 转化为字符串类型
var num2 = 546;
console.log(num2.toString());
结果:
flat(Infinity)函数
eg:
//数组扁平化
let ary = [1, [2, [3, [4, 5]]], 6];
_ary = ary.flat(Infinity);
console.log(_ary); // -> [1, 2, 3, 4, 5, 6]
结果:
replace()函数
eg:
var arr7 = [6, 6, 7, 8, 8, 9];
// 去除字符所有逗号
function clear(str) {
str = str.replace(/,/g, '');
return str;
}
// 返回数字字符串
text = arr7.toString();
console.log(clear(text));
结果:
reverse函数
eg:
// 反向排列
const num = [2, 6, 3, 9, 7];
console.log(num.reverse());
结果:
排序
-1和1决定升降排序 eg:
// 升序排序
function compare1(val1, val2) {
if (val1 > val2) {
return 1;
} else if (val1 < val2) {
return -1;
} else {
return 0;
}
}
let value1 = [0, 1, 66, 10, 5];
console.log(value1.sort(compare1));
// 降序排序
function compare2(val1, val2) {
if (val1 > val2) {
return -1;
} else if (val1 < val2) {
return 1;
} else {
return 0;
}
}
let value2 = [0, 1, 66, 10, 5];
console.log(value2.sort(compare2));
// 冒泡排序
let arr2 = [88, 23, 56, 44, 1, 8];
for (let i = 1; i < arr2.length; i++) {
for (let j = 0; j < arr2.length; j++) {
if (arr2[j] > arr2[j + 1]) {
[arr2[j], arr2[j + 1]] = [arr2[j + 1], arr2[j]]
}
}
}
console.log(arr2);
// 插入排序
let arr3 = [88, 23, 56, 44, 1, 8];
for (let i = 1; i < arr3.length; i++) {
for (let j = i; j > 0; j--) {
if (arr3[j - 1] > arr3[j]) {
[arr3[j - 1], arr3[j]] = [arr3[j], arr3[j - 1]]
}
}
}
console.log(arr3);
// 对数字进行排序,简写
const arr = [3, 2, 4, 1, 5]
arr.sort((a, b) => a - b)
console.log(arr) // [1, 2, 3, 4, 5]
// 对字母进行排序,简写
const arr = ['b', 'c', 'a', 'e', 'd']
arr.sort()
console.log(arr) // ['a', 'b', 'c', 'd', 'e']
结果:
includes函数
eg:
//是否包含
const arr = ['html', 'word', 'doxc'];
const _arr = arr.includes('word'); //返回 true 和 false
if (_arr) {
console.log('存在');
} else {
console.log('不存在');
}
结果:
unshift函数
eg:
// 数组 追加全部到最前面的方法
var arr1 = [{
value: 0,
label: '男'
}, {
value: 1,
label: '女'
}];
var newArr = arr1.unshift({
value: -1,
label: '全部'
})
console.log(arr1);
结果:
slice函数
eg:
// 数组 截取第一个
var arr1 = [{
value: -1,
label: '全部'
}, {
value: 0,
label: '男'
}, {
value: 1,
label: '女'
}];
var newArr = arr1.slice(1);
console.log(newArr);
结果:
map函数
eg:
// 数组 遍历
var arr1 = [{
value: -1,
label: '全部'
}, {
value: 0,
label: '男'
}, {
value: 1,
label: '女'
}];
var newArr = arr1.map(
_item => _item.label
);
console.log(newArr);
结果:
filter函数
eg:
// 数组 筛选
var arr1 = [{
value: -1,
label: '全部'
}, {
value: 0,
label: '男'
}, {
value: 1,
label: '女'
}];
var newArr = arr1.filter(
_item => _item.value !== -1
);
console.log(newArr);
结果:
find函数
eg:
// 数组 匹配
var arr1 = [{
value: -1,
label: '全部'
}, {
value: 0,
label: '男'
}, {
value: 1,
label: '女'
}];
var newArr = arr1.find(
_item => _item.value === -1
);
console.log(newArr);
结果:
防抖
eg:
// 防抖1
let add = document.getElementById('btn');
function fn() {
console.log('ajax请求');
}
function debounce(fun, time) {
let timer;
return function() {
if (timer) clearTimeout(timer)
let args = arguments;
timer = setTimeout(() => {
fun.apply(this, args);
}, time)
}
}
add.addEventListener('click', debounce(fn, 1000));
//模拟实战防抖2
//vue利用loading加载数据模拟select输入调取更新列表数据
//`filterable`属性即可启用搜索功能
//`filter-method`为一个`Function`,它会在输入值发生变化时调用,参数为当前输入值。
<el-select :remote-method="search_componey" @change="init_data(true)" v-model="value" filterable placeholder="请选择" :loading="loading">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </el-option>
</el-select>
data数据:
options:[],
value: '',
methods方法:
init_data(){
...获取列表数据
}
search_componey(value){
console.log(value); //输出搜索输入的内容
if(value){
const _params = {
要传的参数: value
};
this.loading = true;
...调取接口模糊查询数据
this.loading = false;
if(rest.code === 200){
this.options = 接口返回来的数据;
}
}
}
结果防抖1:
拷贝
- JSON.stringify():序列化对象,也可以过滤对象 eg:
let book = {
titile: '西游记',
authors: [
'吴承恩',
'测试'
],
edition: 4,
year: 2017
};
let jsonText = JSON.stringify(book, ['titile', 'edition']);
console.log(jsonText);
console.log(jsonText.titile);
let dee_copy = JSON.parse(jsonText);
console.log(dee_copy.titile);
结果:
- 浅复制:Object.assign(目标对象,源对象) eg:
// 拷贝1 深拷贝
function deep_copy(obj) {
// 转化为对象格式
return JSON.parse(JSON.stringify(obj));
}
const obj = {
name: '测试',
age: 21,
data: 1999 + '-' + 09 + '-' + 18,
sex: ''
}
new_Obj = deep_copy(obj);
console.log(new_Obj);
//拷贝2 浅拷贝
const obj1 = {
name: '测试',
age: 21,
data: 1999 + '-' + 09 + '-' + 18,
sex: '男'
};
const copy = Object.assign(obj1, obj);
console.log(copy);
结果:
检测上传文件后缀是否正确
eg:
// 检测上传文件后缀是否正确
var text = '测试.word';
text_ = text.lastIndexOf('.') + 1;
const texts = text.substring(text_);
console.log(texts); // word
// text是否包含 word
isWord = ['word'].includes(texts) ? true : false;
console.log(aa); //true
结果:
数组去重
eg:
// Set去重
const arr = [1, 2, NaN, '1', , null, 1, 'a', 'b', 'a', NaN, undefined, null];
const newArr2 = [...new Set(arr)];
console.log(newArr2);
// Array.from去重
const newArr = Array.from(new Set(arr))
// indexOf 去重
function resetArr(arr) {
let res = []
arr.forEach(item => {
if (res.indexOf(item) === -1) {
res.push(item)
}
})
return res
}
// 测试
const arr = [1, 1, 2, 3, 3]
console.log(resetArr(arr)) // [1, 2, 3]
// 数组去重
function unique(arr) {
var res = arr.filter((item, index, array) => {
// 通过获取下标和index比较,如果有重复的下标和index会不一致
return array.indexOf(item) === index
})
return res
}
// 测试
console.log(unique([1,'a',55,'a',2,'2',1]));
结果:
栈pop和队列shift
eg:
// 栈方法
var arr = ['000'];
var arrs = arr.push('aaa', 'bbb'); // 添加到数组后面
item = arr.pop();
console.log(item); //取最后一项 bbb
// 队列方法
item = arr.shift();
console.log(item); //取第一项 000
arrs = arr.unshift('ccc', 'ddd'); // 添加到数组前面
item = arr.pop();
console.log(item); //取最后一项 aaa
结果:
splice函数
eg:
// 删除arr数组中第一个aaa
const arr = ['aaa', 'bbb', 'ccc'];
_item = arr.splice(0, 1);
console.log(arr);
结果:
push函数
eg:
// 给数组新增ddd
const arr = ['aaa', 'bbb', 'ccc'];
_item = arr.push('ddd');
console.log(arr);
结果:
join函数
eg:
let arr = ['aa', 'bb', 'cc'];
console.log(arr.join('|')); // aa|bb|cc
console.log(arr.join(',')); // aa,bb,cc
结果:
变量作用域与内存
- js原始值:
Undefined null Boolean Number String Symbol bigint - js引用值:
对象,储存在堆内存上 typeof用来判断一个变量是否为原始数组 注意:值是对象或null 返回objectinstanceof用来判断什么类型的对象和obj构造函数 是返回true 相反false- 重复声明var不会报错 重复声明let会抛出
SyntaxError:已经声明过了 - for循环中避免使用
var声明应该使用let - 开发流程中应该尽可能多使用
const声明 | 提升性能 - js访问局部变量比访问全局变量要快
- 在IE
window.CollectGarbage()立即触发垃圾回收 Opera7及以上版本调用window.opera.collect - ES6中
const和let声明提升性能同时有助于垃圾回收 - 内存泄漏解决:在声明变量头上加
var、let、const关键字
基本引用类型
- toString:接收一个基数的参数并返回相应基数形式的数值字符串 eg:
let num = 10;
console.log(num, 'number类型');
console.log(num.toString(), 'string类型');
结果:
- toFixed: 返回包含指定小数点位数 eg:
let num = 10;
console.log(num.toFixed(2));
结果:
- ES6新增isInteger()方法与安全整数(是不是整数且包含小数点10.00)
字符串函数
concat(): 用于将一个或多个字符串拼接成一个新的字符串
eg:
let stringValue = "hello";
let result = stringValue.concat(" world");
console.log(result); //"hello word"
结果:
substr(): 用于将一个或多个字符串拼接成一个新的字符串
eg:
let stringValue = "hello";
let result = stringValue.substr(2);
console.log(result); //"llo"
结果:
indexOf('要搜索的字符串'): 字符串位置 (要搜索的字符串第一次出现的位置)返回下标
lastIndexOf('要搜索的字符串'): 字符串位置 (要搜索的字符串最后一次出现的位置)返回下标
trim(): 删除前后出现的空格
repeat(): 将字符串复制多少次,然后返回拼接所有副本后的结果
eg:
let stringValue = "2";
let result = stringValue.repeat(2) + '00';
console.log(result); //2200
结果:
toLocaleLowerCase()|toLocaleUpperCase(): 地区特定大小写转换前小后大
eg:
let stringValue = "aS";
let result1 = stringValue.toLocaleLowerCase(); //转换小写
let result2 = stringValue.toLocaleUpperCase(); //转换大写
console.log(result1); //as
console.log(result2); //AS
结果:
集合引用类型
ES6新增创建数组的静态方法:from() 和 of()
- form:将类数组结构转换为数组实例
- of:将一组参数转换为数组实例 eg:
const m = new Map().set(1, 2).set(3, 4);
console.log(m);
console.log(Array.from(m));
const s = new Set().add(1).add(2).add(3);
console.log(s);
console.log(Array.from(s));
// Array.from对现有数组执行浅复制
const a1 = [1, 2, 3, 4];
const a2 = Array.from(a1);
console.log(a2); // [1, 2, 3, 4]
结果:
- 实践中避免使用数组空位,确实要空位用undefined代替
isArray() 检测是否为数组
eg:
// 检测数组
const a1 = [1, 2, 3, 4];
console.log(Array.isArray(a1));
结果:
ES6检索数组内容方法:keys(),values(),entries()
- keys: 返回数组索引迭代器
- values:返回数组元素迭代器
- entries:返回索引/值对迭代器 eg:
// 因为这些方法都返回迭代器
// 所以将他们通过Array.from()转化为数组实例
const a = ['a', 'b', 'c', 'd'];
const aKeys = Array.from(a.keys());
const aValues = Array.from(a.values());
const aEntries = Array.from(a.entries());
console.log(aKeys); //索引
console.log(aValues); //值
console.log(aEntries); //索引/值对
// ES6的解构可以容易在循环中拆分键/值对
for (const [idx, val] of a.entries()) {
console.log(idx);
console.log(val);
}
结果:
fill()填充数组
eg:
// 用5填充整个数组
const _fill = [0, 0, 0, 0, 0];
_fill.fill(5);
console.log(_fill);
_fill.fill(0); //重置
// 用6填充索引大于等于3的元素
_fill.fill(6, 3);
console.log(_fill);
_fill.fill(0); //重置
// 用6填充索引大于等于1的元素且小于3的元素
_fill.fill(7, 1, 3);
console.log(_fill);
结果:
copyWithin()浅复制数组部分内容
- 开始索引和结束索引与fill()使用同样的计算方法 eg:
let ints,
reset = () => ints = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
reset();
//从ints中复制索引0开始的内容,插入索引5开始的位置
ints.copyWithin(5);
console.log(ints);
reset();
//从ints中复制索引5开始的内容,插入索引0开始的位置
ints.copyWithin(0, 5);
console.log(ints);
结果:
toString(),valueOf()转换方法
- toString:数组中有
null,undefined会以空字符表示 eg:
// 转换方法
let colors = ['red', 'blue', 'green', null, undefined];
console.log(colors.toString()); //red,blue,green, ,
console.log(colors.valueOf()); //['red', 'blue', 'green']
console.log(colors); //['red', 'blue', 'green']
结果:
对象、类与面编程向对象
创建Object新实例
eg:
// 创建一个对象
let person = new Object();
person.name = "Marry";
person.age = 23;
person.sex = '女';
person.sayName = function() {
console.log(this.name);
}
let person1 = {
name: "Jack",
age: 24,
sex: '男',
sayName() {
console.log(this.name);
}
}
类的属性
- 修改属性的默认特性必须使用:
Object.defineProperty()
数据属性
- Configurable: 表示属性是否可以通过delete删除并重新定义 默认为true
- Enumerable:表示属性是否可以通过for~in循环返回 默认为true
- Writable:表示属性是否可以被修改 默认为true
- Value:包含属性实际的值 默认为undefined eg:
// writable = false不能修改
let person = {};
Object.defineProperty(person, "name", {
writable: false,
value: "Mary"
});
console.log(person.name); //Mary
person.name = 'Jack'; //不能修改 不会报错 原因writable = false
console.log(person.name); //Mary
结果:
访问器属性
- Configurable: 表示属性是否可以通过delete删除并重新定义 默认为true
- Enumerable:表示属性是否可以通过for~in循环返回 默认为true
- Get:获取函数,在读取属性时调用 默认为undefined
- Set:设置函数,在写入属性时调用 默认为undefined eg:
let book = {
year_: 2017,
edition: 1
};
Object.defineProperty(book, "year", {
get() {
return this.year_;
},
set(newVal) {
if (newVal > 2017) {
this.year_ = newVal;
this.edition += newVal - 2017
}
}
})
book.year = 2018; //修改属性值
console.log(book.edition); //set计算出来的值 2
结果:
定义多个属性
- 一个对象同时定义多个属性:
Object.defineProperties()eg:
let book = {};
Object.defineProperties(book, {
year_: {
value: 2017
},
edition: {
value: 1
},
year: {
get() {
return this.year_;
},
set(newVal) {
if (newVal > 2017) {
this.year_ = newVal;
this.edition += newVal - 2017
}
}
}
})
book.year = 2018; //不能修改属性值 2017
console.log(book.edition); //不能修改 1
结果:
合并对象
- 浅复制:
Object.assign()用于后台管理系统取消操作逻辑 eg:
let dest, src, result;
// 简单复制
dest = {};
src = {
id: 'src'
};
result = Object.assign(dest, src);
console.log(dest === result); // true
console.log(src);
console.log(result);
// 多个源对象
dest = {};
result = Object.assign(dest, {
a: 'foo'
}, {
b: 'bar'
})
console.log(result);
结果:
对象标识及相等判定
- ES新增:
Objec.is()与===很像,必须接收两个参数 eg:
console.log(Object.is(NaN, NaN));
结果:
增强对象语法
属性值简写
eg:
let name = 'Mark';
let persons = {
name
}
console.log(persons);
结果:
可计算属性
eg:
const nameKey = 'name';
const ageKey = 'age';
let person = {};
person[nameKey] = 'Mary';
person[ageKey] = 21;
console.log(person);
结果:
简写方法名
- 简写方法名比ES6的类更有用 eg:
const methodKey = 'sayName';
let person = {
[methodKey](name) {
console.log(`My name is ${name}`);
}
}
person.sayName('Mary');
结果:
对象解构
eg:
let person = {
name: 'Mary',
age: 27
};
let {
name,
age
} = person;
console.log(name); //Mary
console.log(age); //27
结果:
原型模式
- 解决了公共的方法:
prototype共享的属性和方法 - 判断属性是否来自原型链:
hasOwnProperty() - 判断属性是一个原型属性:
in操作符返回true且hasOwnProperty()返回falseeg:
function Person() {};
Person.prototype.name = 'Mary';
Person.prototype.age = 18;
Person.sayName = function() {
console.log(this.name);
}
let person1 = new Person();
let person2 = new Person();
person1.name = 'Jack';
console.log(person1.name); //来自实例
console.log(person1.hasOwnProperty("name"));
console.log("name" in person1);
console.log(person2.name); //来自原型链
console.log(person2.hasOwnProperty("name"));
console.log("name" in person2);
delete person1.name; //删除实例name
console.log(person1.name); //来自原型链
console.log(person1.hasOwnProperty("name"));
console.log("name" in person1);
结果:
对象迭代
- 返回对象值的数组:
Object.values() - 返回对象键/值对的数组:
Object.entries()eg:
const o = {
name: 'aa',
age: 17,
sex: '男'
};
console.log(Object.values(o));
console.log(Object.entries(o));
结果:
代理与反射
- 为开发者提供了拦截并向基本操作嵌入额外行为的能力.可以给目标对象定义一个关联的代理对象,可以作为抽象的目标对象来使用,在对目标对象的各种操作影响目标对象之前,可以在代理对象中的对这些操作加以控制
- 在实际开发中经常会遇到js抛出的错误,但是我们有没有想过自己去接管js异常验证,根据自己的需求抛出异常呢?原本也许不行,但是在es6出来后就可以做到了
创建空代理
- 代理是使用
Proxy构造函数创建的接收两个参数:目标对象和处理程序对象 eg:
const target = {
id: 'target'
};
const handler = {};
const proxy = new Proxy(target, handler);
// id属性会访问同一个值
console.log(target.id);
console.log(proxy.id);
// 给目标属性赋值会反映在两个对象上
// 因为两个对象访问的是同一个值
target.id = 'foo';
console.log(target.id);
console.log(proxy.id);
// 给代理属性赋值会反映在两个对象上
// 因为这个赋值会转移到目标对象
proxy.id = 'bar';
console.log(target.id);
console.log(proxy.id);
// hasOwnProperty()方法在两个地方
// 都会应用到目标对象
console.log(target.hasOwnProperty('id'));
console.log(proxy.hasOwnProperty('id'));
结果:
使用场景
- MVVM的数据双绑
- 这个时候当array数组元素改变的时候,会调用对应的回调。
- target:目标对象
- propertyKey:引用的目标对象上的字符串属性
- value:要赋给属性的值
- receiver:代理对象或继承代理的对象 eg:
function observedArray(cb) {
const array = [];
return new Proxy(array, {
set(target, propertyKey, value, receiver) {
cb(propertyKey, value);
return Reflect.set(target, propertyKey, value, receiver);
}
});
}
const array = observedArray((key, value) => console.log(`${key}: ${value}`));
array.push('a');
array.push('b');
array.push('c');
结果:
函数
箭头函数
- 箭头函数不能使用argments,也不能用作构造函数,也没有prototype属性 eg:
let num = () => {
console.log(arguments[0]);
}
num('han');
结果:
- argments:相当于函数里面的参数 eg:
function sayHi() {
console.log(arguments[0]); //han
console.log(arguments[1]); //aaa
console.log(arguments[2]); //bbb
}
sayHi('han', 'aaa', 'bbb');
结果:
参数扩展
apply:给函数传参- ES6扩展操作符
...:给函数传参 eg:
let values = [1, 2, 3, 4, 5];
function getSum() {
let sum = 0;
for (let i = 0; i < arguments.length; i++) {
sum += arguments[i];
}
return sum;
}
console.log(getSum.apply(this, values)); //apply传参
console.log(getSum(...values)); //ES6扩展运算符传参
结果:
函数内部 arguments和this
- arguments.callee:让函数逻辑与函数名解耦 避免递归函数出错 eg:
function text(num) {
if (num <= 1) {
return 1;
} else {
return num * arguments.callee(num - 1); //避免递归函数出错
}
}
console.log(text(5)); //120
let _text = text; //将text赋给_text 这时指针改变方向指向_text
text = function() {
return 0;
}
console.log(_text(5)); //text指针指向_text 输出120
console.log(text(5)); // 0
结果:
- this:特殊的对象 eg:
window.color = 'red';
let o = {
color: 'blue'
};
function sayColor() {
console.log(this.color);
}
//结果1
sayColor(); //red
o.sayColor = sayColor;
o.sayColor(); //blue this指向o
//结果2 箭头函数
// let sayColor = () => console.log(this.color); //箭头函数是在window上下文定义的
// sayColor(); //red
// o.sayColor = sayColor;
// o.sayColor(); //red
结果1:
结果2:
- 解决在事件或定时回调中调用某函数:this指向的并非想要的对象。此时将
回调函数写成箭头函数可以解决问题 eg:
function King() {
this.sayName = 'Mary';
// this引用King的实例
setTimeout(() => {
console.log(this.sayName);
}, 1000);
};
function Queen() {
this.sayName = 'Jack';
// this引用window对象
setTimeout(function() {
console.log(this.sayName);
}, 1000);
}
new King();
new Queen();
结果:
函数属性与方法
- call(),apply()作用一样,只是传参形式不同
- 如何用取决于传参更方便:直接传
arguments对象或者数组apply()否则call() apply接收的是一个包含多个参数的数组, 而call方法接收的是若干个参数列表。bind接收的是若干个参数列表,但必须要调用一次 eg:
function sum(num1, num2) {
return num1 + num2;
}
function callSum(num1, num2) {
return sum.call(this, num1, num2);
}
console.log(callSum(10, 10));
function callSum1(num1, num2) {
return sum.apply(this, arguments);
}
console.log(callSum1(10, 10));
function callSum2(num1, num2) {
return sum.apply(this, [num1, num2]); //传入数组
}
console.log(callSum2(10, 10));
结果:
- call()和apply()最强大地方:控制函数调用上下文函数体内this值的能力
- 好处:可以将任意对象设置为任意函数的作用域
- ES5bind():创建一个新函数实例,this值会被绑定到传给bind()的对象 eg:
window.color = 'red';
let o = {
color: 'blue'
};
function sayColor() {
console.log(this.color);
}
sayColor(); //red
sayColor.call(this); //red
sayColor.call(window); //red
sayColor.call(o); //blue
sayColor.apply(this); //red
sayColor.apply(window); //red
sayColor.apply(o); //blue
let objSayColor = sayColor.bind(o);
objSayColor(); //blue
结果:
函数表达式
- 函数声明和函数表达式之间的区别,关键是理解提升 eg:
let isEdit = false;
// 千万别这样做
if (isEdit) {
function sayHi() {
console.log('Hi!');
}
} else {
function sayHi() {
console.log('Yo!');
}
}
let sayHi;
// 没问题
if (isEdit) {
sayHi = function() {
console.log('Hi!');
}
} else {
sayHi = function() {
console.log('Yo!');
}
}
sayHi();
结果:
递归函数
- 通常形式是一个函数通过名称调用自己 eg:
function factorial(num) {
if (num <= 1) {
return 1;
} else {
return num * factorial(num - 1);
}
}
let _factorial = factorial;
factorial = null;
console.log(_factorial(4));
结果:
- 为了上面递归出错:写递归函数时使用arguments.callee可以避免上面的问题
- arguments.callee:在严格模式下不能访问
arguments.calleeeg:
function factorial(num) {
if (num <= 1) {
return 1;
} else {
return num * arguments.callee(num - 1);
}
}
let _factorial = factorial;
factorial = null;
console.log(_factorial(4)); //24
结果:
- arguments.callee:用匿名函数代替
- 解决在严格模式下访问
arguments.callee出错 eg:
const factorial = (function f(num) {
if (num <= 1) {
return 1;
} else {
return num * f(num - 1); //用匿名函数代替
}
});
console.log(factorial(4)); //24
结果:
尾调用优化
- 以递归为例 eg:
function fib(n) {
if (n < 2) {
return n;
}
return fib(n - 1) + fib(n - 2)
}
console.log(fib(6)); //8
结果:
- 优化递归 eg:
// 基础框架
function fib(n) {
return fibImpl(0, 1, n);
}
// 执行递归
function fibImpl(a, b, n) {
if (n === 0) {
return a;
}
return fibImpl(b, a + b, n - 1)
}
console.log(fib(6)); //8
结果:
回调函数
- 把一个函数的定义当作参数传递给另一个函数 eg:
function eat(food, callback) {
callback(food);
}
eat('水果捞', function(food) {
console.log(`拿着${food}放着糖吃`);
})
结果:
闭包
- 好处:
局部变量不随着原函数的销毁而销毁 - 闭包是指那些引用了另一个函数作用域中变量的函数,通常是在嵌套中实现的
- 函数执行完毕不会释放变量,仍在闭包函数中保存,等待下一次使用 eg:
function createCounter() {
let counter = 0;
const myFunction = function() {
counter = counter + 1;
return counter;
}
return myFunction;
}
const increment = createCounter()
const c1 = increment(); //1
const c2 = increment(); //2
const c3 = increment(); //3
console.log(c1, c2, c3); //1,2,3
// ES6形式闭包
let c = 4
const addX = x => n => n + x
const addThree = addX(3);
let d = addThree(c);
console.log(d);
// 常规
let c = 4
function addX(x) {
return function(n) {
return n + x
}
}
const addThree = addX(3)
let d = addThree(c)
console.log(d)
// 闭包
function text() {
var a = 0;
return function(num) {
a += num;
console.log(a);
}
}
var inner = text();
inner(1);
inner(1);
inner(1);
// 自执行函数
var inners = (function() {
var a = 0;
return function(num) {
a += num;
console.log(a);
}
})();
inners(2);
inners(2);
inners(2);
结果:
期约和异步函数
异步编程
- 异步行为是为了优化因计算量大而时间长的操作。
- 在等待其他操作完成的同时,即使运行其他指令,系统也能保存稳定。
- 异步操作不一定要计算量大或者时间长,不想为等待某个操作而阻塞线程执行,那么任何时候都可以使用。
同步与异步
- 同步行为对应内存中顺序执行的处理器指令。
- 异步行为类似于系统中断,即当前进程外部的实体可以出发代码执行。
- 异步操作是必要的,因为强制等待一个长时间操作不可行。
以往的异步编程模式
- 以往只支持定义回调函数表明异步操作完成。
- 需要深度嵌套回调函数,因此产生「回调地狱」
嵌套异步函数
- 噩梦一般的 嵌套😈
期约
期约基础
- 引用类型Promise,可以用new操作符来实例化
- 创建时需要传入执行器函数作为参数
期约状态机
-
三种状态
-
待定(pending)
- 最初始状态,待定状态下可以落定(settled)为代表成功兑现状态,或者失败的拒绝状态
- 状态修改不可逆
- 不能保证期约必然会脱离待定状态
-
兑现(fulfilled,有时也称解决,resolved)
-
拒绝(rejected)
-
-
期约的状态是私有的,不能外部检测到,为了避免读取到期约状态,用同步的方式处理期约对象。
-
期约的状态是不能被外部修改。
Promise.resolve
- 通过调用Promise.resolve()静态方法,可以实例化一个解决的期约 eg:
// p1 和 p2实际上一样
let p1 = new Promise((resolve, reject) => resolve());
let p2 = Promise.resolve();
setTimeout(console.log, 0, Promise.resolve()); //undefind
setTimeout(console.log, 0, Promise.resolve(3)); //3
// 多余的参数忽略
setTimeout(console.log, 0, Promise.resolve(4, 5, 6)); //4
结果:
Promise.reject
- 与Promise.resolve()类似。Promise.reject()会实例化一个拒绝的期约并抛出一个异步错误(这个错误不能通过try/cat捕获,只能通过拒绝处理程序捕获) eg:
// p1 和 p2实际上一样
let p1 = new Promise((resolve, reject) => reject());
let p2 = Promise.reject();
let p = Promise.reject(3);
setTimeout(console.log, 0, p); // 3
p.then(null, (e) => setTimeout(console.log, 0, e)); //3
setTimeout(console.log, 0, Promise.reject(Promise.resolve()));
结果:
同步/异步执行的二元性
eg:
try {
throw new Error('foo');
} catch (e) {
console.log(e);
}
try {
Promise.reject(new Error('bar'));
} catch (e) {
console.log(e);
}
结果:
Promise.prototype.then()
- 为期约实例添加处理程序的主要方法
- then() 方法
- 最多接收两个参数 onResolved和onRejected处理程序
- 都是可选的,如果提供的话则会在期约分别进入"兑现"和“拒绝”状态时执行。
- 如果想提供onRejected参数,就要在onResolved参数位置传入undefind,有助于避免在内存中创建多余对象
Promise.prototype.catch()
- 用于给期约添加拒绝处理程序,直接收一个参数onRejected处理程序。
- 语法糖,相当于调用Promise.prototype.then(null, onRejected)
异步函数
async
- async 关键词用于声明异步函数,可用在函数声明、函数表达式、箭头函数
- 使用async可让函数具有异步特征,但总体上仍然是同步求值的
- 使用async关键字可以让函数具有异步特征,在实际中它需要和await配合使用。
await
- 一旦定义了一个函数作为一个异步函数,我们就可以使用 await 关键词。这个关键词放在回调的Promise之前,将会暂停执行函数,直到Promise执行或拒绝。
- await 关键字会暂停执行异步函数后面的代码,它这个行为和生成器函数中的yield关键字是一样的,await关键字也是解包对象的值,任何将这个值传给表达式,再用异步恢复异步执行的操作。
- 异步函数里不包含await关键字,和普通函数没区别
await的限制
- 必须在异步函数中使用,不能在顶级上下文使用。
- 可以定义并立即调用
- 异步函数特质不会扩展到潜逃函数,await只能直接出现在异步函数的定义中
综合
eg:
function doubleAfter2seconds(num) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2 * num)
}, 2000);
} )
}
async function testResult() {
let result = await doubleAfter2seconds(30);
console.log(result);
}
testResult();
结果:
BOM
- BOM的核心 ----- window对象
- 通过location对象获取页面信息
- 使用navigator对象了解浏览器
- 通过history对象操作浏览器历史
window对象
- window对象在浏览器中身份
- ECMAScript中的
Global对象 - 浏览器窗口的JavaScript接口
- ECMAScript中的
Clobal作用域
- 通过var声明的所有全局变量和函数都会变成window对象的属性和方法 eg:
var age = 29;
console.log(window.age);
结果:
- 使用let或const替代var,这不会把变量添加到全局对象
窗口关系
- top对象始终指向最上层,即浏览器窗口本身
- parent对象则始终指向当前窗口的父窗口
- 如果当前窗口是最上层窗口,则parent等于top
- 最上层window如果不是通过window.open()打开的,那么其name属性不会包含值
- self对象是始终window属性会始终指向window
窗口位置和像素比
- screenLeft 和 screenTop属性,表示窗口相对于屏幕左侧和顶部的位置
- moveTo和moveBy方法移动窗口 eg:
// 把窗口移动到左上角
window.moveTo(0,0);
// 把窗口向下移动100像素
window.moveBy(0,100);
窗口大小
- 返回浏览器窗口中页面视口的大小:innerWidth innerHeigth
- 返回浏览器窗口自身的大小:outerHeigth outerWidth
- resizeTo() resizeBy() 调整窗口大小
- resizeTo()接收新的宽度和高度值
- resizeBy()接收宽度和高度各要缩放多少 eg:
- resizeTo() resizeBy() 调整窗口大小
// 返回页面视口的宽度和高度
var a = document.documentElement.clientWidth;
console.log(a);
var b = document.documentElement.clientHeight;
console.log(b);
结果:
视口位置
- scroll() scrollTo() scrollBy() 方法滚动页面 eg:
// 相当于当前视口向下滚动100像素
window.scrollBy(0,100);
导航与打开新窗口
- window.open():导航到指定URL
window.open('http://www.baidu.com');
定时器
- setTimeout():指定在一定时间后执行某代码
- clearTimeout():取消等待中的排期任务 eg:
setTimeout(() => {
console.log('你好我的电脑');
}, 1000);
结果:
- setInterval():指定每隔一段时间执行某代码 eg:
setInterval(() => {
console.log('你好我的电脑');
}, 1000);
结果:
系统对话框
- alter():警告框
- confirm():确认框
- prompt():提示框
location对象
- 修改hash的值会在浏览器历史记录增加一条新纪录
- 最好把reload()作为最后一行代码 eg:
// 假设当前URL为http://www.wrox.com/WileyCDA/
// 把URL修改为http://www.wrox.com/WileyCDA/#section1
location.hash = "#section1";
// 把URL修改为http://www.wrox.com/WileyCDA/?q=javascript
location.search = "?q=javascript";
// 把URL修改为http://www.somewhere.com/WileyCDA/
location.hostname = "www.somewhere.com"
// 把URL修改为http://www.somewhere.com/mydir/
location.pathname = "mydir"
// http://www.somewhere.com:8080/WileyCDA/
location.port = 8080;
// 重定向之后不能回到前一页
location.replace("http://www.wrox.com/");
// 重新加载 可能是从缓存加载
location.reload();
// 重新加载 从服务器加载
location.reload(true);
navigator对象
- 通常用于确定浏览器的类型
history对象
- 表示当前窗口首次使用以来用户的导航历史记录
导航
- history.go(-1) = history.back():后退一页
- history.go(1) = history.forward():前进一页
- history.go(2):前进两页
DOM
节点层级
- doucument节点表示每个文档的根节点
- 文档元素:根节点的唯一子节点是
<html>元素
Node类型
- 每个节点都有nodeType属性,表示该节点类型
nodeName与nodeValue
- nodeName与nodeValue保存着有关节点的信息
- 值取决于节点类型
节点关系
- 每个节点都有一个childNodes属性
- 如果childNodes中只有一个节点:它的previousSibling和nextSibling属性都是null
- 父节点和它的第一个及最后一个字节点都有专门属性:firstChild和lastChild分别指向childNodes中的第一个和最后一个字节点
操作节点
- 最常用的方法是appendChild():用于在childNodes列表末尾添加节点,返回新添加的节点
- insertBefore():把节点放到childNodes特定位置而不是末尾。
- appendChild()和insertBefore()在插入节点时不会删除任何已有节点
- replaceChild()接收2参数:要插入的节点和要替换的节点
- removeChild():移除节点
Document类型
- 用于获取关于页面的信息及操作其外观和底层结构
定位元素
- getElementByid():要获取元素的ID eg:
<div id="div">测试</div>
let div = document.getElementById('div');
console.log(div);
结果:
- getElementsByTagName():获取元素引用的方法
eg:
<img src="aaaa.html" alt="">
<img src="v-bind.html" alt="">
<img src="v-for.html alt="">
let imgs = document.getElementsByTagName("img");
console.log(imgs,length); // 3
console.log(imgs[1].src);
结果:
- getElementsByName():获取给定name属性的所有元素
eg:
<input type="radio" value="red" name="color" id="colorRed">
<input type="radio" value="blue" name="color" id="colorBlue">
<input type="radio" value="green" name="color" id="colorGreen">
let radios = document.getElementsByName('color');
console.log(radios);
结果:
特殊集合
- document.anchors():包含文档所有带name属性的
<a>元素 - document.forms():包含文档所有
<form>元素 - document.images():包含文档所有
<img>元素 - document.links():包含文档所有带href属性的
<a>元素
文档写入
- document.write():向文档中输出内容
Element类型
- 表示XML或HTML元素
DOM扩展
Selectors API
- 核心方法:
querySelector()和querySelectorAll()
querySelector()
- 接收css选择符参数,返回匹配该模式的第一个后代元素,没有匹配返回null eg:
<div>测试1</div>
<div>测试2</div>
<div>测试3</div>
let div1 = document.querySelector("div");
console.log(div1);
let div2 = document.querySelector('#div');
console.log(div2);
let div3 = document.querySelector('.div');
console.log(div3);
结果:
querySelectorAll()
- 返回所有匹配节点,返回的是一个NodeList实例
- 避免了使用NodeList对象可能造成的性能问题 eg:
<div id="myDiv">
<em>测试1</em>
<em>测试2</em>
<em>测试3</em>
</div>
<script>
// 取得ID为"myDiv"的<div>元素中的所有<em>元素
let ems = document.getElementById('myDiv').querySelectorAll('em');
console.log(ems);
// 取得所有类名中包含"selected"的元素
let selecteds = document.querySelectorAll('.selected');
// 取得所有是<p>元素子元素<strong>元素
let strongs = document.querySelectorAll('p strong');
</script>
结果:
元素遍历
- childElementCount:返回子元素数量
- firstElementChild:指向第一个Element类型的子元素
- lastElementChild:指向最后一个Element类型的子元素
- previousSibling:指向前一个Element类型的同胞元素
- nextSibling:指向后一个Element类型的同胞元素
HTML5
CSS类扩展
- getElementsByClassName():返回类名中包含相应类的元素的NodeList
classList属性
- IE10及以上版本(部分)和其他主流浏览器实现了classList属性
- 操作类名,通过className属性实现添加删除和替换 每次操作后要重新设置这个值才生效 eg:
<div class="bd user disabled"></div>
<script>
let div = document.querySelector("div");
// 要删除"user"类
let targetClass = "user";
// 把类名拆成数组
let className = div.className.split(/\s+/);
// 找到要删除类的索引
let idx = className.indexOf(targetClass);
// 如果有删除
if (idx > -1) {
className.splice(idx, 1);
}
// 重新设置类名
div.className = className.join(" ");
console.log(div.className);
</script>
结果:
- add:向类名列表中添加指定的字符串value。有的话什么也不做
- contains:返回布尔值,表示给定的value是否存在
- remove:从类名列表中删除指定的字符串值value
- toggle:类名列表中存在指定value则删除,不存在则添加
优化eg:
<div class="bd user disabled"></div>
<script>
let div = document.querySelector("div");
// 要删除"user"类
div.classList.remove('user');
console.log(div.className);
</script>
结果:
焦点管理
eg:
<input type="button" id="btn" value="">
<script>
let btn = document.getElementById('btn');
btn.focus();
console.log(document.activeElement === btn);
</script>
结果:
插入标记
- innerHTML属性innerText eg:
<div id="btn"></div>
<script>
let btn = document.getElementById('btn');
btn.innerHTML = 'hello word!';
</script>
结果:
DOM2和DOM3
样式
| css属性 | js属性 |
|---|---|
| background-image | style.backgroundImage |
| color | style.color |
| display | style.display |
| font-family | style.fontFamily |
eg:
<div id="div"></div>
<script>
let div = document.getElementById('div');
div.style.backgroundColor = "green";
div.style.width = "100px";
div.style.height = "100px";
</script>
结果:
事件
- JavaScript与HTML的交互是通过事件实现的
事件流
- 描述了页面接收事件的顺序
事件冒泡
- IE事件流被称为事件冒泡。事件被定义为从最具体的元素开始触发,然后向上传播至没有那么具体的元素
- 从下往上
DOM事件流
- DOM2规定事件流分为3个阶段:事件捕获、到达目标、事件冒泡
事件处理程序
- 为响应事件而调用的函数称为事件处理程序(或事件监听器)
- 事件处理程序的名字以
on开头
- 事件处理程序的名字以
HTML事件处理程序
eg:
<input type="button" value="11111" onclick="console.log('HTML事件处理程序')">
DOM0事件处理程序
eg:
<button id="btn">DOM0事件处理程序</button>
<script>
let btn = document.getElementById('btn');
btn.onclick = function() {
console.log(this.id); // id
}
btn.onclick = null; //移除事件处理
</script>
DOM2事件处理程序
- DOM2 Events为事件处理程序的赋值和移除定义的两个方法
- addEventListener()和removeEventListener()暴露在所有DOM节点上,接收3个参数
- 事件名、事件处理函数和一个布尔值
- true:表示在捕获阶段调用事件处理程序
- false:表示在冒泡阶段调用事件处理程序 eg:
- addEventListener()和removeEventListener()暴露在所有DOM节点上,接收3个参数
<button id="btn">DOM2事件处理程序</button>
<script>
let btn = document.getElementById('btn');
btn.addEventListener('click', () => {
console.log('DOM2事件处理程序'); // DOM2事件处理程序
}, false)
</script>
IE事件处理程序
- attachEvent()detachEvent() eg:
<button id="btn">IE事件处理程序</button>
<script>
let btn = document.getElementById('btn');
btn.attachEvent('onclick', function() {
console.log('IE事件处理程序'); // IE事件处理程序
})
</script>
事件对象
- DOM中发生事件时所有相关信息都会被收集并储存在event的对象中
DOM事件对象
- event对象只在事件处理程序执行期间存在,一旦执行完毕,就会被销毁
- 在事件处理程序内部
- this对象始终等于currentTarget的值
- target只包含事件的实际 eg:
<button id="btn">DOM事件对象</button>
<script>
let btn = document.getElementById('btn');
btn.onclick = function(event) {
console.log(event.type); // click
console.log(event.currentTarget === this); // true
console.log(event.target === this); // true
}
</script>
// target属性等于按钮本身
<button id="btn">移动到我身上变红</button>
<script>
let btn = document.getElementById('btn');
let handler = function(event) {
switch (event.type) {
case "click":
console.log('clicked');
break;
case "mouseover":
btn.style.backgroundColor = 'red';
break;
case "mouseout":
event.target.style.backgroundColor = '';
break;
}
}
btn.onclick = handler;
btn.onmouseover = handler;
btn.onmouseout = handler;
<script>
IE事件对象
- 如果事件处理程序是使用DOM0方式制定的:event对象只是window对象一个属性
事件类型
- 用户界面事件:涉及与BOM交互的通用浏览器事件
- 焦点事件:在元素获得和失去焦点时触发
- 鼠标事件:使用鼠标在页面上执行某些操作时的触发
- 滚轮事件:使用鼠标滚轮时触发
- 输入事件:向文档输入文本时触发
- 键盘事件:使用键盘在页面上执行某些操作时触发
- 合成事件:在使用某种IME输入字符时触发
用户界面事件
- DOMActivate:元素被用户通过鼠标或键盘操作激活时触发
- load:在window上当页面加载完成后触发
- unload:在window上页面完全卸载后触发
- abort:在
<object>元素上当相应对象加载完成前被用户提前终止下载时触发 - error:在window上当JavaScript报错时触发
- select:在文本框(
<input>或textarea)上用户选择一个或多个字符时触发 - resize:在window或窗格上当窗口或窗格被缩放时触发
- scroll:当用户滚动包含滚动条的元素时在元素上触发 eg:
window.addEventListener("load", (event) => {
console.log('Loaded!'); // Loaded!
})
焦点事件
- focus:当元素获取焦点时触发
- blur::当元素失去焦点时触发
鼠标和滚轮事件
- click:在用户单击鼠标主键(左键)或回车时触发
- dbclick:在用户双击鼠标主键(左键)时触发
- mousedown:在用户按下任意鼠标键时触发
- mouseenter:在用户把鼠标光标从元素外部移到内部时触发
- mouseleave:在用户把鼠标光标从元素内部移到外部时触发
- mousemove:在鼠标在元素上移动时反复触发
- mouseout:在用户把鼠标光标从一个元素移到另一个元素上时触发
- mouseover:在用户把鼠标光标从元素外部移到内部时触发
- mouseup:在用户释放鼠标键时触发
- 页面的所有元素都支持鼠标事件,除了mouseenter和mouseleave,所有鼠标事件都会冒泡,都可以取消
键盘与输入事件
- keydown:用户按下键盘某个键时触发
- keypress:用户按下键盘上某个键并产生字符时触发,而且持续按住会重复触发
表单脚本
表单基础
- acceptCharset:服务器可以接收的字符集。等价于HTML的accept-charset属性
- action:请求的URL,等价于HTML的action
- elements:表单中所有控件的HTMLCollection
- enctype:请求的编码类型,等价于HTML的enctype属性
- length:表单控件的数量
- method:HTTP请求的方法类型,通常是get或post,等价于HTML的method属性
- name:表单的名字,等价于HTML的name属性
- reset:把表单字段重置为各自的默认值
- submit:提交表单
- target:用于发送请求和接收响应的窗口名字,等价于HTML的target属性
- 公共属性
- document.forms获取页面上所有表单元素
- event.preventDefault()阻止表单提交
- disable:布尔值,表示表单字段是否禁用
- from:指针,指向表单字段所属的表单。(只读属性)
- name:字符串、这个字段的名字
- type:字符串,表示字段类型,如radio、checkbox
- value:要提交给服务器的字段值
- 公共事件
- blur:在字段失去焦点时触发
- change:
<input>元素的value发生变化且失去焦点时触发,<select>选中选项触发 - focus:在字段获得焦点时触发
- 公共属性
文本框编程
- 在HTML有两种文本框的方式:单行使用 input 和 多行使用 textarea
- required:必填字段
- HTML5新增type值:email 和 url
- pattern:用于指定一个正则表达式,用户输入的文本必须与之匹配 eg:
<form id="form" action="" method="post">
密码:<input type="password" id="pwd" value="123456" name="password"><br>
<input type="radio" name="sex" value="男">男
<input type="radio" name="sex" checked value="女">女
</form>
<script>
let form = document.getElementById('form');
let filed1 = form.elements['sex'].value;
console.log(filed1);
let filed2 = form.elements['password'].value;
console.log(filed2);
let pwd = document.getElementById('pwd');
let pwdValue = pwd.value;
console.log(pwdValue);
let radio = form.elements[1].type;
console.log(radio);
let name = form.elements[0].name;
console.log(name);
</script>
结果:
JSON
- 理解JSON语法
- 解析JSON
- JSON序列化
语法
- 简单值:字符串、数值、布尔值和null可以在JSON中出现。特殊值undefined不可以
- 对象:第一种复杂数据类型,对象表示有序键/值对。每个值可以是简单值,也可以是复杂类型
- 数组:第二种复杂数据类型,数组表示可以通过数值索引访问的值的有序列表。
简单值
- JSON字符串必须使用双引号
- 布尔值和null本身也是有效的JSON值 eg:
"hello word!"
对象
- JSON的对象必须使用双引号把属性名包围起来 eg:
{
"name": "测试",
"age": 29
}
数组
- 数组在JSON中使用JavaScript的数组字面量形式表示 eg:
[25, "测试", true];
解析与序列化
JSON对象
- JSON对象有俩个方法:
stringfy()和parse()JSON.stringfy()把一个JavaScript对象序列化为一个JSON字符串- 输出不包括空格或缩进的JSON字符串
- JSON字符串直接传给
JSON.parse()得到相应的JavaScript对象 eg:
let book = {
title: "西游记",
authors: [
"吴承恩",
"六小龄童"
],
edition: 4,
year: 2017
};
let jsonText = JSON.stringify(book);
console.log(jsonText, 'jSON字符串');
let bookCopy = JSON.parse(jsonText);
console.log(bookCopy, 'js对象');
结果:
网络请求与远程资源
使用XHR
- 使用XHR对象要调用
open():请求类型("get"、"post"等)、请求URL,以及是否异步的布尔值 - 只能访问同源URL:域名、端口、协议相同 eg:
xhr.open("get", "index.php", false);
- 发送定义好的请求,必须调用
send()方法 - 不需要发送请求体,必须传
nulleg:
xhr.open("get", "index.php", false);
xhr.send(null);
- responseText:作为响应体返回的文本
- responseXML:响应数据的XML DOM文档
- status:相应的HTTP状态
- statusText:相应的HTTP状态描述
HTTP头部
- Cookie:页面中设置的Cookie
- Host:发送请求页面所在的域
- Referer:发送请求页面的URL
GET请求
- 向服务器查询某些信息。必要时在get请求的URL后面添加查询字符串参数
- 查询字符串的每个名和值必须使用encodeURLComponent()编码,名/值必须以和号(&)分隔 eg:
xhr.open("get", "index.php?name1=value1&name2=value2", true);
POST
- POST请求比GET请求占用更多资源。
- 性能方面:发送相同数量的数据,GET请求比POST请求要快2倍 eg:
xhr.open("post", "index.php", true);
FormData类型
- FormData:对表单数据进行序列化
超时
- IE8给XHR增加timeout:用于表示发请求后等待多少毫秒,如果响应不成功就中断请求
跨源资源共享
- Origin:
http://www.text.net - 如果服务器决定响应请求,应该发送Access-Control-Allow-Origin头部,包含相同的源;或者如果资源是公开的,那么就包含"*"。
- 无论是请求还是响应都不会包含cookie信息 eg:
Access-Control-Allow-Origin: http://www.text.net
JSONP跨域
- JSONP包含两部分:回调和数据 eg:
callback({"name": "Nicholas"})
// JSONP请求
http://index.net/json/?callback=handleResponse
原理:通过script里面的src属性
解决跨域问题
- 修改响应头 跨域请求:
res.header("Access-Control-Allow-Origin","*") - jsonp 跨域请求:回调函数callback eg:
<h1>Hello!!</h1>
<script>
// 1.修改响应头 跨域请求
res.header("Access-Control-Allow-Origin","*")
// 2.jsonp 跨域请求
var app = express();
app.get("/", function(req, res) {
var funcname = req.query.callback;
res.send(funcname + "('你好')");
// fn('你好')
})
</script>
<script>
function fn(data) {
alert(data);
}
</script>
<script src="http://localhost:80?callback=fn"></script>
Fetch API
错误处理与调试
try/catch 语句
eg:
try {
// 可能出错的代码
} catch (error) {
// 出错时要做什么
}
finally 子句
- 只要代码包含了finally子句。try块a或catch块中的return会被忽略 eg:
try {
return 2;
} catch (error) {
return 1;
} finally {
return 0;
}
客户端储存
cookie
- 在客户端储存会话信息
限制
- 与特定域绑定的
cookie的构成
- 名称:唯一标识 不区分大小写
- 值:必须经过URL编码
- 编码:
encodeURIComponent() - 解码:
decodeURIComponent()
- 编码:
- 域:有效的域
- 路径:请求URL中包含这个路径才会把cookie发送到服务器
- 过期时间:何时删除cookie的时间戳
- 安全标志:设置之后,只在使用SSL安全连接的情况下才会把cookie发送到服务器
Web Storage
- 解决通过客户端不需要频繁发送回服务器的数据时使用cookie的问题
- localStorage:永久储存机制
- sessionStorage:跨会话的储存机制
Storage类型
- 用于保存名/值对数据。
- clear():删除所有值,不在Firefox中实现
- getItem(name):取得name的值
- key(index):取得给定数值位置的名称
- removeItem(name):删除给定name名/值对
- setItem(name,value):设置给定name的值
sessionStorage对象
- 只储存会话数据,数据只会储存到浏览器关闭。
- 只能由最初存储数据的页面使用,多页面程序中用处有限
- 通过使用setItem()方法或直接给属性赋值给它添加数据 eg:
// 储存数据
sessionStorage.setItem("name", "Mary");
// 取出数据
let name = sessionStorage.getItem("name");
console.log(name);
// 删除值
sessionStorage.removeItem("name");
结果:
localStorage对象
- 同一个域,在相同端口上使用相同的协议
- 不受页面刷新影响,也不会因关闭窗口、标签页或重新启动浏览器而丢失 eg:
// 储存数据
localStorage.setItem("name", "Mack");
// 取出数据
let mack = localStorage.getItem("name");
console.log(mack);
结果:
vue登录验证token
eg:
// 路由守卫
// '/login':初始页面
// to:即将要进入的目标路由对象 from:即将要离开的路由对象; next 进行管道中的下一个钩子
router.beforeEach((to, from, next) => {
// 取token值
let token = localStorage.getItem("token");
if (token || to.path === '/login') {
next();
} else {
next('/login');
}
})