技术标签: Javascript 前端 javascript
本文集成多篇博客以及权威书籍加上自己的理解汇总,以下是借鉴的博客网址:
https://blog.csdn.net/qq_33277654/article/details/122924692
https://blog.csdn.net/cc18868876837/article/details/81211729
https://blog.csdn.net/hualvm/article/details/84395850
https://blog.csdn.net/nicexibeidage/article/details/78144138
后盾人网站:https://doc.houdunren.com
《js红宝书》
6+1 USONB
6种简单数据类型(原始类型):undefined、null、boolean、number、string、symbol。
*基本数据类型是存储在栈中,栈内存是自动分配内存的,赋值时传值。
1种复杂数据类型:Object对象(细分有Object、Array、Function、Date、RegExp)。
*复杂数据类型时存储在堆中,堆内存是动态分配的,不会自动释放,赋值时传址。
1)typeof操作符
需要注意的是typeof是一个操作符而不是函数,因此不需要参数。
调用typeof null时返回的是“object”,这是因为特殊值null被认为是一个对空对象的引用。
严格来讲函数在js里被认为是对象,而不是数据类型,但函数有自己的特性,因此需要通过typeof来区分函数和其他对象。
2)Undefined类型
该类型只有一个值就是特殊值undefined。当使用var或let声明了变量但没有初始化时,就相当于给变量赋予了undefined值(const在声明后必须赋值否则报错)。
增加undefined这个特殊值的目的就是为了正式明确空对象指针null和未初始化变量的区别。
当输出未初始化变量和未声明变量时,会出现下述情况:输出未初始化变量a的值为undefined,要输出未声明变量b则会报错。
let a;
// let b;
console.log(a);
console.log(b);
对于未声明的变量,只能执行一个有用的操作,就是对它调用typeof。未初始化的变量a的类型是undefined,而对未声明的变量b调用结果还是undefined。
let a;
// let b;
console.log(typeof a);
console.log(typeof b);
3)Null类型
该类型也同样只有一个特殊值null,null表示一个空对象指针,因此typeof null会返回object。
undefined值是由null值派生而来的,表面上两者相等,用等于操作符(==)比较时始终返回true,但要注意这个操作符会为了比较而转换它的操作数。
console.log(null == undefined);
但两者用途完全不一样。永远不用显式地将变量值设为undefined,但null不是这样,如果变量要保存对象而当时有没有哪个对象可以保存,就可以用null来填充。
4)Boolean类型
有两个字面量值:true和false。这两个布尔值不同于数值,true不等于1,false不等于0。
类型转换:Boolean()
数据类型 | 转换为true的值 | 转换为false的值 |
---|---|---|
String | 非空字符串 | “” |
Number | 非零数值 | 0、NaN |
Object | 任意对象 | null |
5)Number类型
(1)浮点值
定义浮点值必须包含小数点,且小数点后面必须至少有一个数字。虽然小数点前面不是必须有整数,但是推荐加上。比如:
let a = 1.1;
let b = 0.1;
let c = .1; //不推荐
存储浮点值使用的内存空间是存储整数值的两倍,所以js会想方设法把浮点值转为整数。小数点后面没有数字或者小数点后的数字是0,那么就会被转换为整数。比如:
let a = 10.; //小数点后面没有数,被当为整数
let b = 1.0; //小数点后面是0,被当为整数
对于过大或者过小的数值,浮点值可以用科学计数法来表示。js中科学计数法格式要求是一个数值后跟一个大写或小写的字母e,再加上一个要乘的10的多少次幂。比如:
let num = 3.15e7; //3.15乘10的7次幂=31500000
console.log(num);
浮点值精确的最高可达17位小数,但远不如整数精确。例如0.1+0.2得到的不是0.3,由于这种微小的舍入错误,导致很难测试特定的浮点值。之所以存在这种舍入错误是因为使用了IEEE 754数值,其他使用相同格式的语言也有这个问题。
(2)值的范围
js可以表示的最小数值保存在Number.MIN_VALUE
中,最大数值保存在Number.MAX_VALUE
中。如果计算得到的数值超出了js可表示范围,那么这个数值会被自动转换为一个特殊的Infinity值。正Infinity或者负Infinity值将不能再进一步用于任何计算,要确定一个值是否为有限大,可以使用isFinite()
函数。如下:
let max = Number.MAX_VALUE;
let min = Number.MIN_VALUE;
console.log(max); //1.7976931348623157e+308
console.log(min); //5e-324
let result = max + max;
console.log(isFinite(result));//false
(3)NaN
Not a Number,用于表示本来要返回数值的操作失败了。
在js里,0、+0、-0相除会返回NaN。
console.log(0 / 0); //NaN
console.log(0 / -0); //NaN
console.log(0 / +0); //NaN
如果分子是非0值,分母是有符号0或无符号0,则会返回Infinity或-Infinity。
console.log(5 / -0); //-Infinity
console.log(5 / +0); //Infinity
NaN有几个特殊属性:
1.任何涉及NaN的操作始终返回NaN,在连续多步计算时可能是个问题。
2.NaN不等于包括NaN在内的任何值。
console.log(NaN / 10); //NaN
console.log(NaN == NaN); //false
为此js提供了isNaN()
函数。该函数接收一个参数,可以是任意数据类型,然后判断这个参数是否“不是数值”。该函数会尝试把传入的值转换为数值。
console.log(isNaN(NaN)); //true
console.log(isNaN(10)); //false,10是数值
console.log(isNaN("10")); //false,可以转换为数值10
console.log(isNaN("blue")); //true,不可以转换为数值
console.log(isNaN(true)); //false,可以转换为数值1
(4)类型转换为数值
Number()
pareseInt()
parseFloat()
Number()可以用于任何数据类型,后两个函数主要用于将字符串转换为数值。
Number():
console.log(Number(true)); //1
console.log(Number(3)); //3
console.log(Number(null)); //0
console.log(Number(undefined)); //NaN
console.log(Number("011")); //11
console.log(Number("Hello World")); //NaN
parseInt():
更专注于字符串是否包含数值模式,字符串最前面的空格会被忽略,从第一个非空格字符开始转换。如果第一个字符不是数值字符、加号或减号,parseInt()立刻返回NaN,依次检测至字符串末尾或非数值字符。(意味着空字符串会返回NaN)
console.log(parseInt("")); //NaN
console.log(parseInt("1234blue")); //1234
console.log(parseInt("22.5")); //22
console.log(parseInt("70")); //70
parseFloat():
跟parseInt()类似。parseFloat()始终忽略字符串开头的0,如果字符串表示整数(没有小数点或者小数点后面只有一个0),则返回整数。
console.log(parseFloat("1234blue")); //1234
console.log(parseFloat("22.5")); //22.5
console.log(parseFloat("22.5.49")); //22.5
console.log(parseFloat("0987.6")); //987.6
console.log(parseFloat("3.15e7")); //31500000
6)String类型
(1)转换为字符串
toString()
String()
+""
toString():
几乎所有值都有的toString方法,唯一的用途就是返回当前值的字符串等价物。字符串本身也有toString()方法,但只是简单地返回自身的一个副本。null和undefined值没有toString()方法。
String():
如果不确定值是不是null或undefined,可以使用String()转型函数。
(2)模板字面量
es6新增特性,模板字面量保留换行字符,可以跨行定义字符串。
let a = 'first line\nsecond line';
let b = `first line
second line`;
console.log(a);
//first line
//second line
console.log(b);
//first line
//second line
使用${}
实现字符串插值
(3)模板字面量标签函数
模板字面量通过定义标签函数来自定义插值行为。
对于有n个插值的模板字面量,传递给标签函数表达式参数个数始终是n,而传给标签函数第一个参数所包含的字符串个数始终是n+1.(通过${}划分)
let a = 6;
let b = 9;
function simpleTag(strings, ...expressions) {
console.log(strings);
for (const exp of expressions) {
console.log(exp);
}
return 'foobar';
}
let untaggedResult = `${
a}+${
b}=${
a + b}`;
let taggedResult = simpleTag`${
a}+${
b}=${
a + b}`;
//['', '+', '=', '']
//6
//9
//15
console.log(untaggedResult);//6+9=15
console.log(taggedResult);//foobar
7)Symbol类型
8)Object类型
js中对象通过new创建。
每个Object都有如下属性和方法:
constructor:
用于创建当前对象的函数。
hasOwnProperty(propertyName):
用于判断当前实例(不是原型)上是否存在给定的属性。
isPrototypeOf(object):
用于判断当前对象是否为另一个对象的原型。
propertyIsEnumerable(propertyName):
用于判断给定属性是否可以使用for-in语句枚举。
toString():
返回对象的字符串表示。
valueOf():
返回对象对应的字符串、数值或布尔值表示。
1)let、const声明变量
let声明变量特点:
const声明常量特点:
2)变量解构赋值
const F4 = ["大哥","二哥","三哥","四哥"];
let [a,b,c,d] = F4; // 这就相当于我们声明4个变量a,b,c,d,其值分别对应"大哥","二哥","三哥","四哥"
console.log(a + b + c + d); // 大哥二哥三哥四哥
const F3 = {
name : "大哥",
age : 22,
sex : "男",
xiaopin : function(){
console.log("我会演小品!");
}
}
let {
name,age,sex,xiaopin} = F3; // 注意解构对象这里用的是{}
console.log(name + age + sex + xiaopin); // 大哥22男
xiaopin(); // 此方法可以正常调用
3)模板字面量
4)对象的简化写法
允许在大括号里直接写入变量和函数作为对象的属性和方法。
4. 完整写法
5. 简化写法(当对象属性名和变量名相同时)
6. 声明方法的简化
5)箭头函数
特性:
(1)定时器
传统函数的问题:定时器setTimeout()和setInterval()的回调函数中this的指向都是window。这是因为JS的定时器方法是定义在window下的,调用的代码运行在与所在函数完全分离的执行环境上。
let ad = document.getElementById('ad');
ad.addEventListener('click', function () {
setTimeout(function () {
console.log(this); //window
this.style.background = 'pink';
}, 2000);
});
使用箭头函数可以解决:因为箭头函数的this指向的是父级函数指向的this,也就是div元素。
let ad = document.getElementById('ad');
ad.addEventListener('click', function () {
setTimeout(() => {
console.log(this); //div元素
this.style.background = 'pink';
}, 2000);
});
(2)事件处理函数
事件处理函数使用箭头函数的时候,this指向的是父级函数指向的this,也就是window
let ad = document.getElementById('ad');
ad.addEventListener('click', () => {
console.log(this); //window
});
(3)对象方法函数
func1:是普通函数,因此this指向当前对象obj
func2:是箭头函数,this指向父级函数的this指向,因此是window
func4:是箭头函数,this指向父级函数func3的this指向,是obj
const obj = {
name: '张三',
age: 18,
sex: '男',
fun1: function () {
console.log(this)
},
fun2: () => {
console.log(this)
},
fun3: function () {
const fun4 = () => {
console.log(this);
}
fun4();
}
};
obj.fun1(); //obj
obj.fun2(); //window
obj.fun3(); //obj
6)函数参数的默认值
es6允许给函数参数赋值初始值
7)rest参数(…args)
ES6 引入 rest 参数,用于获取函数的实参,用来代替 arguments
ES5获取实参使用arguments参数,而且获得的是Object类型。
注意:
// ES6的rest参数...args,rest参数必须放在最后面
function data(...args){
console.log(args); // fliter some every map
}
data("大哥","二哥","三哥","四哥");
8)扩展运算符(…)
…扩展运算符能将 “数组” 转换为 “逗号分隔的参数序列”,好比rest参数的逆运算,对数组进行解包。
两者的区别:
//声明一个数组 ...
const tfboys = ['易烊千玺', '王源', '王俊凯'];
// 声明一个函数
function chunwan() {
console.log(arguments);}
chunwan(...tfboys); // chunwan('易烊千玺','王源','王俊凯')
具体应用:
(1)数组的合并
const kuaizi = ['王太利','肖央']; const fenghuang = ['曾毅','玲花'];
// es5传统的合并方式
// const zuixuanxiaopingguo = kuaizi.concat(fenghuang);
const zuixuanxiaopingguo = [...kuaizi, ...fenghuang];
console.log(zuixuanxiaopingguo);
(2)数组的克隆
如果有引用类型,则是浅拷贝。
const sanzhihua = ['E','G','M'];
const sanyecao = [...sanzhihua];// ['E','G','M']
console.log(sanyecao);
(3)将伪数组转为真正的数组
const divs = document.querySelectorAll('div');
const divArr = [...divs];
console.log(divArr); // arguments
9)Symbol类型
ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。
类似于身份证,参数代表姓名,姓名可以重复,但是身份证号不可以。
特点:
// 没有参数的情况
let name1 = Symbol();
let name2 = Symbol();
name1 === name2 // false
name1 === name2 // false
// 有参数的情况
let name1 = Symbol('flag');
let name2 = Symbol('flag');
name1 === name2 // false
name1 === name2 // false
//不能与其他数据进行运算
//let result = s + 100;
//let result = s > 100;
//let result = s + s;
//报错
(1)Symbol的方法
Symbol.for()
用于将描述相同的Symbol变量指向同一个Symbol值,方便通过描述区分不同的Symbol。
Symbol.for("foo"); // 创建一个 symbol 并放入 symbol 注册表中,键为 "foo"
Symbol.for("foo"); // 从 symbol 注册表中读取键为"foo"的 symbol
Symbol.for("bar") === Symbol.for("bar"); // true,证明了上面说的
Symbol("bar") === Symbol("bar"); // false,Symbol() 函数每次都会返回新的一个 symbol
var sym = Symbol.for("mario");
sym.toString();
// "Symbol(mario)",mario 既是该 symbol 在 symbol 注册表中的键名,又是该 symbol 自身的描述字符串
Symbol()和Symbol.for()的不同点:
(2)用Symbol向对象中添加方法
要向对象里添加方法,但是怕已经存在同名方法,因此使用Symbol
let game = {
name: '俄罗斯方块',
up: function () {
},
down: function () {
}
};
//要向game对象里添加方法,但是怕已经存在同名方法,因此使用Symbol
//法一:
let method = {
up: Symbol(),
down: Symbol()
};
game[method.up] = function () {
console("new up");
};
game[method.down] = function () {
console("new down");
};
console.log(game);
//{name: '俄罗斯方块', up: ƒ, down: ƒ, Symbol(): ƒ, Symbol(): ƒ}
// down: ƒ ()
// name: "俄罗斯方块"
// up: ƒ ()
// Symbol(): ƒ ()
// Symbol(): ƒ ()
//法二:
let newgame = {
name: "狼人杀",
[Symbol("up")]: function () {
},
[Symbol("down")]: function () {
}
}
console.log(newgame);
//{name: '狼人杀', Symbol(up): ƒ, Symbol(down): ƒ}
// name: "狼人杀"
// Symbol(down): ƒ ()
// Symbol(up): ƒ ()
(3)Symbol的内置值
除了定义自己使用的 Symbol 值以外,ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法。可以称这些方法为魔术方法,因为它们会在特定的场景下自动执行。
Symbol内置值的使用,都是作为某个对象类型的属性去使用,用来控制对象的表现。
内置Symbol的值 | 调用时机 |
---|---|
Symbol.hasInstance | 当其他对象使用 instanceof 运算符,判断是否为该对象的实例时,会调用这个方法 |
Symbol.isConcatSpreadable | 对象的 Symbol.isConcatSpreadable 属性等于的是一个布尔值,表示该对象用于 Array.prototype.concat()时,是否可以展开。 |
Symbol.species | 创建衍生对象时,会使用该属性 |
Symbol.match | 当执行 str.match(myObject) 时,如果该属性存在,会调用它,返回该方法的返回值。 |
Symbol.replace | 当该对象被 str.replace(myObject)方法调用时,会返回该方法的返回值。 |
Symbol.search | 当该对象被 str. search (myObject)方法调用时,会返回该方法的返回值。 |
Symbol.split | 当该对象被 str. split (myObject)方法调用时,会返回该方法的返回值。 |
Symbol.iterator | 对象进行 for…of 循环时,会调用 Symbol.iterator 方法,返回该对象的默认遍历器 |
Symbol.toPrimitive | 该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。 |
Symbol. toStringTag | 在该对象上面调用 toString 方法时,返回该方法的返回值。 |
Symbol. unscopables | 该对象指定了使用 with 关键字时,哪些属性会被 with环境排除。 |
Symbol.hasInstance
class Person {
static [Symbol.hasInstance](param) {
console.log(param);
console.log("我被用来检测类型了");
return false;
}
}
let o = {
name: 'Alison' };
console.log(o instanceof Person);
//Object name: "Alison"
//我被用来检测类型了
//false
Symbol.isConcatSpreadable
const arr = [1, 2, 3];
const arr2 = [4, 5, 6];
// 合并数组:false数组不可展开,true可展开
arr2[Symbol.isConcatSpreadable] = false;
console.log(arr.concat(arr2));
//[1, 2, 3, Array(3)]
10)迭代器iterator接口
**迭代器可以用来自定义遍历对象。**它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。
ES6 创造了一种新的遍历命令 for…of 循环,Iterator 接口主要供 for…of 消费。
原生具备 iterator 接口的数据(可用 for of 遍历):
工作原理:
// 声明一个数组
const xiyou = ['唐僧', '孙悟空', '猪八戒', '沙僧'];
// 使用 for...of 遍历数组
for (let v of xiyou) {
console.log(v);
}
let iterator = xiyou[Symbol.iterator]();
//调用对象的next方法
console.log(iterator.next());//{value: '唐僧', done: false}
console.log(iterator.next());//{value: '孙悟空', done: false}
console.log(iterator.next());//{value: '猪八戒', done: false}
console.log(iterator.next());//{value: '沙僧', done: false}
console.log(iterator.next());//{value: undefined, done: true}
//重新初始化对象,指针也会重新回到最前面
let iterator1 = xiyou[Symbol.iterator]();
console.log(iterator1.next());//{value: '唐僧', done: false}
迭代器自定义遍历对象:
// 声明一个对象
const banji = {
name: "终极一班",
stus: ['xiaoming', 'xiaoning', 'xiaotian', 'knight'],
[Symbol.iterator]() {
// 索引变量
let index = 0;
// 保存this
let _this = this;
return {
next: function () {
if (index < _this.stus.length) {
const result = {
value: _this.stus[index], done: false };
// 下标自增
index++;
// 返回结果
return result;
} else {
return {
value: undefined, done: true };
}
}
};
}
}
// 遍历这个对象
for (let v of banji) {
console.log(v);
}
//xiaoming xiaoning xiaotian knight
11)Promise
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大,Promise 将异步操作队列化,按照期望的顺序执行,返回符合预期的结果,可以通过链式调用多个 Promise 达到我们的目的。所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件的结果(通常是一个异步操作的结果)。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。
Promise特点:
(1)基本用法
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
// Promise
// Hi!
// resolved
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve
函数和reject
函数。
resolve
函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
reject
函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
(2)then
一个promise 需要提供一个then方法访问promise 结果,then 用于定义当 promise 状态发生改变时的处理,即promise处理异步操作,then 用于结果。
promise 就像 kfc 中的厨房,then 就是我们用户,如果餐做好了即 fulfilled ,做不了拒绝即rejected 状态。那么 then 就要对不同状态处理。
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("操作成功");
}, 2000);
});
const p2 = new Promise((resolve, reject) => {
resolve(p1);
}).then(
msg => {
console.log(msg);
},
error => {
console.log(error);
}
);
如果只关心成功则不需要传递 then 的第二个参数,如果只关心失败时状态,then 的第一个参数传递 null。
const promise = new Promise((resolve, reject) => {
reject("is error");
});
promise.then(null, error => {
console.log(`失败:${
error}`);
});
promise 传向then的传递值,如果then没有可处理函数,会一直向后传递。
let p1 = new Promise((resolve, reject) => {
reject("rejected");
})
.then()
.then(
null,
f => console.log(f)
);
Promise解决过程也可以处理其他类型,如果该类型有then方法,程序会把他封装成promise进行处理。
let p1 = new Promise((resolve, reject) => {
resolve("fulfilled");
}).then(
value => {
return class {
static then(resolve, reject) {
resolve("这是一个静态方法");
}
};
},
reason => {
}
).then(value => {
console.log(value);
})
链式调用:每一个then都是对前一个返回的Promise的处理。每次的 then 都是一个全新的 promise,默认 then 返回的 promise 状态是 fulfilled。如果 then 返回promise 时,后面的then 就是对返回的 promise 的处理,需要等待该 promise 变更状态后执行。
(3)catch
catch用于失败状态的处理函数,等同于 then(null,reject){}
错误是冒泡的操作的,下面没有任何一个then 定义第二个函数,将一直冒泡到 catch 处理错误。
new Promise((resolve, reject) => {
reject(new Error("请求失败"));
})
.then(msg => {
})
.then(msg => {
})
.catch(error => {
console.log(error);
});
catch 也可以捕获对 then 抛出的错误处理。
new Promise((resolve, reject) => {
resolve();
})
.then(msg => {
throw new Error("这是then 抛出的错误");
})
.catch(() => {
console.log("33");
});
catch 也可以捕获其他错误,下面在 then 中使用了未定义的变量,将会把错误抛出到 catch。
new Promise((resolve, reject) => {
resolve("success");
})
.then(msg => {
console.log(a);
})
.catch(reason => {
console.log(reason);
});
但像下面的在异步中 throw 将不会触发 catch,而使用系统错误处理。
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
throw new Error("fail");
}, 2000);
}).catch(msg => {
console.log(msg + "后盾人");
});
(4)finally
无论状态是resolve 或 reject 都会执行此动作,finally 与状态无关。
const promise = new Promise((resolve, reject) => {
reject("hdcms");
})
.then(msg => {
console.log("resolve");
})
.catch(msg => {
console.log("reject");
})
.finally(() => {
console.log("resolve/reject状态都会执行");
});
(5)resolve
有时需要将现有对象转为 Promise 对象,使用 promise.resolve()
方法可以快速的返回一个Promise对象。(等价于new Promise(resolve => resolve()))
promise.resolve()
方法的参数分四种情况:
Promise.resolve("后盾人").then(value => {
console.log(value); //后盾人
});
(6)reject
和 Promise.resolve 类似,reject 生成一个失败的promise。
new Promise(resolve => {
resolve("后盾人");
})
.then(v => {
if (v != "houdunren.com") return Promise.reject(new Error("fail"));
})
.catch(error => {
console.log(error);
});
(7)all
Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。使用Promise.all 方法可以同时执行多个并行异步操作,比如页面加载时同时获取课程列表与推荐课程。
const p = Promise.all([p1, p2, p3]);
只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。const hdcms = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("第一个Promise");
}, 1000);
});
const houdunren = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("第二个异步");
}, 1000);
});
const hd = Promise.all([hdcms, houdunren])
.then(results => {
console.log(results);
})
.catch(msg => {
console.log(msg);
});
// ['第一个Promise', '第二个异步']
注意,如果作为参数的 Promise 实例,自己定义了catch方法,那么它一旦被rejected,并不会触发Promise.all()的catch方法。
const p1 = new Promise((resolve, reject) => {
resolve('hello');
})
.then(result => result)
.catch(e => e);
const p2 = new Promise((resolve, reject) => {
throw new Error('报错了');
})
.then(result => result)
.catch(e => e);
Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));
// ["hello", Error: 报错了]
上面代码中,p1会resolved,p2首先会rejected,但是p2有自己的catch方法,该方法返回的是一个新的 Promise 实例,p2指向的实际上是这个实例。该实例执行完catch方法后,也会变成resolved,导致Promise.all()方法参数里面的两个实例都会resolved,因此会调用then方法指定的回调函数,而不会调用catch方法指定的回调函数。
(8)allSettled
有时候,我们希望等到一组异步操作都结束了,不管每一个操作是成功还是失败,再进行下一步操作。Promise.all()方法只适合所有异步操作都成功的情况,如果有一个操作失败,就无法满足要求。为了解决这个问题,引入了Promise.allSettled()
方法,用来确定一组异步操作是否都结束了(不管成功或失败)。
Promise.allSettled()方法接受一个数组作为参数,数组的每个成员都是一个 Promise 对象,并返回一个新的 Promise 对象。只有等到参数数组的所有 Promise 对象都发生状态变更(不管是fulfilled还是rejected),返回的 Promise 对象才会发生状态变更。
const p1 = new Promise((resolve, reject) => {
resolve("resolved");
});
const p2 = new Promise((resolve, reject) => {
reject("rejected");
});
Promise.allSettled([p1, p2])
.then(msg => {
console.log(msg);
})
//(2) [{…}, {…}]
// 0: {status: 'fulfilled', value: 'resolved'}
// 1: {status: 'rejected', reason: 'rejected'}
// length: 2
(9)race
使用Promise.race() 处理容错异步,和race单词一样哪个Promise快用哪个,哪个先返回用哪个。
const hdcms = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("第一个Promise");
}, 2000);
});
const houdunren = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("第二个异步");
}, 1000);
});
Promise.race([hdcms, houdunren])
.then(results => {
console.log(results);
})
.catch(msg => {
console.log(msg);
});
//第二个异步
12)Set&WeakSet
ES6 提供了新的数据结构 Set(集合)。它类似于数组,但成员的值都是唯一的,集合实现了 iterator 接口,所以可以使用『扩展运算符』和『for…of…』进行遍历。
特点:
Set的属性和方法:
size
返回集合的元素个数;add
增加一个新元素,返回当前集合;delete
删除元素,返回 boolean 值;has
检测集合中是否包含某个元素,返回 boolean 值;clear
清空集合,返回 undefined;基本用法:
let set = new Set([2, 3, 4]);
set.add(1);
set.add(1);
set.add('1');
console.log(set);
(1)数组去重
使用展开运算符
let arr = [1, 1, 2, 3, 4, 4, 5, 6];
//使用展开运算符
let result = [...new Set(arr)];
console.log(result);//[1, 2, 3, 4, 5, 6]
(2)交集
let set1 = new Set([1, 3, 5, 5, 7, 9]);
let set2 = new Set([1, 1, 2, 4, 5, 6, 7]);
let newSet = new Set([...set1].filter(item => set2.has(item)));
console.log(newSet);//Set(3) {1, 5, 7}
(3)并集
let set1 = new Set([1, 3, 5, 5, 7, 9]);
let set2 = new Set([1, 1, 2, 4, 5, 6, 7]);
let union = new Set([...set1, ...set2]);
console.log(union); //Set{1,3,5,7,9,2,4,6}
(4)差集
交集的取反。
需要注意是谁和谁求差集,比如集合1和集合2求差集,就是1里面有的,2里面没的。
let set1 = new Set([1, 3, 5, 5, 7, 9]);
let set2 = new Set([1, 1, 2, 4, 5, 6, 7]);
let newSet = new Set([...set1].filter(item => !set2.has(item)));
console.log(newSet);//Set(2) {3, 9}
WeakSet特性:
13)Map&WeakMap
ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合。但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map 也实现了iterator 接口,所以可以使用『扩展运算符』和 『for…of…』进行遍历。具有极快的查找速度。
Map 的属性和方法:
size
返回 Map 的元素个数set
增加一个新元素,返回当前 Map,支持链式操作get
返回键名对象的键值has
检测 Map 中是否包含某个元素,返回 boolean 值clear
清空集合,返回 undefined// 创建一个空 map
let m = new Map();
// 创建一个非空 map
let m2 = new Map([['name', 'Alison'], ['slogon', '狗狗']]);
// 1. size 返回 Map 的元素个数;
console.log(m2.size);
// 2. set 增加一个新元素,返回当前 Map;
m.set("皇帝", "大哥");
m.set("丞相", "二哥");
console.log(m);
// 3. get 返回键名对象的键值;
console.log(m.get("皇帝"));
// 4. has 检测 Map 中是否包含某个元素,返回 boolean 值;
console.log(m.has("皇帝"));
// 5. clear 清空集合,返回 undefined;
m.clear();
console.log(m);
WeakMap特性:
14)class类
ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过 class 关键字,可以定义类。基本上,ES6 的 class 可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
知识点:
class
声明类;constructor
定义构造函数初始化;extends
继承父类;super
调用父级构造方法;static
定义静态方法和属性;(1)声明类
ES5:
function Phone(brand, price) {
this.brand = brand;
this.price = price;
}
// 添加方法
Phone.prototype.call = function () {
console.log("我可以打电话");
}
// 实例化对象
let HuaWei = new Phone('华为', 5999);
HuaWei.call();//我可以打电话
console.log(HuaWei);//Phone {brand: '华为', price: 5999}
ES6:
class Phone {
// 构造函数,名字是固定的
constructor(brand, price) {
this.brand = brand;
this.price = price;
}
// 党法必须使用该方式写,不能使用call:function(){}
call() {
console.log("我可以打电话");
}
}
let HuaWei = new Phone('华为', 5999);
HuaWei.call();//我可以打电话
console.log(HuaWei);//Phone {brand: '华为', price: 5999}
(2)类静态成员
类也是一个对象,他也有他自己的静态属性,即不会与实例对象公用的属性。
ES5:
function Phone() {
}
// 类对象Phone的静态成员
Phone.nickname = "手机";
Phone.change = function () {
console.log("改变世界");
}
let nokia = new Phone();
console.log(nokia.name);//undefined
nokia.change();//Uncaught TypeError: nokia.change is not a function
console.log(Phone.nickname);//手机
Phone.change();//改变世界
// 实例对象的属性
Phone.prototype.color = "黑色";
console.log(nokia.color);//黑色
ES6:
class Phone {
static nickname = "手机";
static change() {
console.log("改变世界");
}
}
let nokia = new Phone();
console.log(nokia.nickname);//undefined
nokia.change();//Uncaught TypeError: nokia.change is not a function
(3)类的继承
ES5:构造函数实现继承
// 父类:手机
function Phone(brand, price) {
this.brand = brand;
this.price = price;
}
Phone.prototype.call = function () {
console.log("打电话");
}
// 子类:智能手机
function SmartPhone(brand, price, color, size) {
Phone.call(this, brand, price);
this.color = color;
this.size = size;
}
// 设置子类构造函数的原型
SmartPhone.prototype = new Phone;
// 声明子类的方法
SmartPhone.prototype.photo = function () {
console.log("拍照");
}
SmartPhone.prototype.play = function () {
console.log("玩游戏");
}
const HuaWei = new SmartPhone("华为", 5999, "银色", "5.5inch");
HuaWei.call();//打电话
HuaWei.photo();//拍照
HuaWei.play();//玩游戏
ES6:类继承
class Phone {
constructor(brand, price) {
this.brand = brand;
this.price = price;
}
call() {
console.log("我可以打电话");
}
}
class SmartPhone extends Phone {
// 构造函数
constructor(brand, price, color, size) {
super(brand, price); //调用父类构造函数
this.color = color;
this.size = size;
}
photo() {
console.log("我可以拍照");
}
game() {
console.log("我可以玩游戏");
}
}
const xiaomi = new SmartPhone("小米", 1999, "黑色", "5.15inch");
console.log(xiaomi);//SmartPhone {brand: '小米', price: 1999, color: '黑色', size: '5.15inch'}
xiaomi.call();//我可以打电话
xiaomi.photo();//我可以拍照
xiaomi.game();//我可以玩游戏
(4)子类对父类方法重写
class Phone {
constructor(brand, price) {
this.brand = brand;
this.price = price;
}
call() {
console.log("我可以打电话");
}
}
class SmartPhone extends Phone {
// 构造函数
constructor(brand, price, color, size) {
super(brand, price); //调用父类构造函数
this.color = color;
this.size = size;
}
photo() {
console.log("我可以拍照");
}
game() {
console.log("我可以玩游戏");
}
call() {
console.log("子类的打电话");
}
}
const xiaomi = new SmartPhone("小米", 1999, "黑色", "5.15inch");
console.log(xiaomi);//SmartPhone {brand: '小米', price: 1999, color: '黑色', size: '5.15inch'}
xiaomi.call();//子类的打电话
xiaomi.photo();//我可以拍照
xiaomi.game();//我可以玩游戏
(5)getter和setter
class Phone {
get price() {
console.log("价格属性被读取");
return 5999;
}
set price(value) {
console.log("价格属性被修改");
}
}
let p = new Phone();
console.log(p.price);//价格属性被读取 //5999
p.price = 6000;//价格属性被修改
JavaScript 语言的一大特点就是单线程,也就是说同一个时间只能处理一个任务。为了协调事件、用户交互、脚本、UI 渲染和网络处理等行为,防止主线程的不阻塞,(事件循环)Event Loop的方案应用而生。
JavaScript 处理任务是在等待任务、执行任务 、休眠等待新任务中不断循环中,也称这种机制为事件循环。
js中有两个异步任务队列:宏任务队列和微任务队列
注意点:
1)原理分析
setTimeout(() => {
console.log("定时器");
new Promise(resolve => {
console.log("settimeout Promise");
resolve();
}).then(() => {
console.log("settimeout then");
});
}, 0);
new Promise(resolve => {
//同步
console.log("Promise");
resolve();
}).then(() => {
//微任务
console.log("then");
});
console.log("后盾人");
//Promise 后盾人 then 定时器 settimeout Promise settimeout then
2)脚本加载
引擎在执行同步任务时不会进行DOM渲染,建议将script 放在 BODY 结束标签前,这样会先渲染DOM后再执行任务,页面就不会白页。
<body>
<h1>houdunren.com</h1>
<script src="hd.js"></script>
</body>
3)定时器
定时器计时从程序运行开始,到时放入宏任务队列中,等待同步任务执行后才能执行。HTML标准规定最小时间不能低于4毫秒,有些异步操作如DOM操作最低是16毫秒,总之把时间设置大些对性能更好。
JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
都是window下的方法
1)setTimeout()
在指定的毫秒数后调用函数或计算表达式,仅执行一次,一般用于延迟执行某方法或功能。
setTimeOut在执行其所要调用的方法时除了计算参数时间(时间间隔)还要等待方法执行时间。
setTimeout(function[, delay, arg1, arg2, ...]);
function sayHi(phrase, who) {
alert(phrase + ', ' + who);
}
setTimeout(sayHi, 1000, "Hi", "Alison");//参数是函数名
setTimeout('sayHi("Hi","Alison")', 1000);//参数是函数名
销毁方法:clearTimeout(id); 创建定时器时会返回一个id
2)setInterval()
按照指定的周期(以毫秒计)来调用函数或计算表达式,方法会不停的调用函数,直到clearInterval()被调用或者窗口被关闭,用于刷新表单。
setInterval在执行其所要调用的方法时严格按照参数时间(时间间隔)执行,不会等待方法执行时间,如果执行方法的时间超过了参数时间(时间间隔),则setInverval将取消下次执行,进行下下次的方法调用。
function sayHi(phrase, who) {
alert(phrase + ', ' + who);
}
setInterval(sayHi, 2000, "Hi", "Alison");//参数是函数名
setInterval('sayHi("Hi","Alison")', 2000);//参数是函数名
销毁方法:clearInterval(id); 创建定时器时会返回一个id
3)用setTimeout,不用setInterval
由于setInterval的执行时间不会等待方法执行时间,因此当方法执行时间大于设定时间时,会产生“丢帧”现象,从而导致不同定时器的代码执行间隔比预期小。
箭头函数是普通函数的简写,但是它不具备很多普通函数的特性。
使用立即执行函数可以模拟块级作用域,即在一个函数表达式内部声明变量,然后立即调用这个函数。这样位于函数体作用域的变量就像是在块级作用域中一样。ECMAScript 5尚未支持块级作用域,使用立即执行函数模拟块级作用域是相当普遍的。
(function () {
for (var i = 0; i < count; i++) {
console.log(i);
}
})();
console.log(i); // 抛出错误:外部访问不到立即执行函数作用域中的变量i
传递参数:
(function (who) {
console.log("I miss you, " + who)
})("kangkang")
(function (global) {
console.log(global)
})(this)
通常全局变量被作为一个参数传递给立即执行参数,这样它在函数内部不使用window也可以被访问到。但不应该给立即执行函数传递太多的参数。
这部分我是看了一位大佬的文章之后敲敲代码豁然开朗的。此处附上链接:https://blog.csdn.net/cc18868876837/article/details/81211729
提到原型,初学的时候经常会被__proto__和prototype搞得十分混乱。__proto__一般被称为隐式原型,prototype一般被称为显式原型。
牢记:
此处附上大佬画的图:
函数创建的对象.__proto__ ===该函数.prototype
该函数.prototype.constructor===该函数本身
1)__proto__隐式原型
__proto__属性是由一个对象指向一个对象,也就是指向他们的原型对象。
作用:就是当访问一个对象属性,该对象内部并不存在这个属性,他就会通过__proto__攀升原型链,找他的原型对象是否有该属性,直到原型链顶端null,然后报错。
通过__proto__属性向上攀升找原型对象直到null这样的一条链,就是我们所谓的原型链。我们平时调用的字符串方法、数组方法、对象方法、函数方法等都是靠__proto__继承而来的。
2)prototype显式原型
prototype属性是由一个函数指向一个对象,也就是这个函数所创建的实例的原型对象(也可以理解为函数看作为对象)。因此f1.__proto__ === Foo.prototype
作用:包含所有实例化对象的公共属性和方法。
任何函数在创建的时候都会默认创建该函数的prototype对象。
3)constructor属性
consturctor属性是由一个对象指向一个函数,也就是指向这个对象的构造函数,其中Function对象比较特殊,他的构造函数就是他自己这个Function()方法,因此constructor属性的终点就是Function这个函数。
constructor这个属性来讲,只有prototype对象才有。其他对象都是通过__proto__继承prototype对象而得来的属性。
下面是敲代码验证:
function Foo() {
};
let f1 = new Foo();
console.log(f1.__proto__);//是对象,是实例化对象f1指向f1的原型对象
console.log(Foo.prototype); //是对象,是Foo()这个函数指向Foo.prototype对象(类似于把Foo()函数看作是Foo对象,因为js里函数也是对象)
//函数创建的对象.__proto__===该函数.prototype
console.log(f1.__proto__ === Foo.prototype);//f1原型对象就是Foo.prototype这个函数对象
console.log(f1.constructor);//是函数,是由实例化对象f1指向该对象的构造函数Foo(),f1对象本身没有constructor属性,只有prototype对象才有,但是f1.__proto__===Foo.prototype,因此f1原型链上有constructor方法
console.log(Foo.prototype.constructor);//是函数,是由Foo.prototype对象指向该对象构造函数Foo()
console.log(f1.constructor === Foo);
//该函数.prototype.constructor===该函数本身
console.log(Foo.prototype.constructor === Foo);
原型继承、组合继承、寄生组合继承、es6新特性extends
1)原型继承
Object.create(作为新对象原型的对象[,给新对象定义额外属性的对象])
本质上是对传入的对象执行了一次浅拷贝(仅仅复制对象的引用)
优点:不需要单独创建构造函数,在对象间共享信息。
缺点:子类实例共享了父类构造函数的引用属性。
只有第一个参数:
let person = {
name: "Alison",
friends: ["Ruby", "Andy"]
};
let anotherPerson = Object.create(person);
anotherPerson.name = "Yooo";
anotherPerson.friends.push("Robo");
let yetAnotherPerson = Object.create(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
console.log(person.name);//Alison
console.log(person.friends);//['Ruby', 'Andy', 'Robo', 'Barbie']引用类型值共享
添加第二个参数:
let person = {
name: "Alison",
friends: ["Ruby", "Andy"]
};
let anotherPerson = Object.create(person, {
name: {
value: "Greg"
}
});
console.log(anotherPerson.name);//Greg
2)组合继承
思路是使用原型链继承原型上的属性和方法,而通过盗用构造函数继承实例属性。
优点:既可以把方法定义在原型上实现重用,又可以让每个实例有自己的属性。
缺点:效率问题:父类构造函数始终会被调用两次:一次在是创建子类原型时调用,另一次是在子类构造函数中调用。
function SuperType(name) {
this.name = name;
this.colors = ["red", "green", "blue"];
}
SuperType.prototype.sayName = function () {
console.log(this.name);
}
function SubType(name, age) {
SuperType.call(this, name); //继承属性,第二次调用
this.age = age;
}
SubType.prototype = new SuperType(); //继承方法,第一次调用
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function () {
console.log(this.age);
};
let instance1 = new SubType("Yooo", 29);
instance1.colors.push("black");
console.log(instance1.colors);//['red', 'green', 'blue', 'black']
instance1.sayName();//Yooo
instance1.sayAge();//29
3)寄生组合继承
基本思路是不通过调用父类构造函数给子类原型赋值new SuperType()
,而是取得父类原型的一个副本Object.create(SuperType.prototype)
。说到底就是使用寄生式继承来继承父类原型,然后将返回的新对象赋值给子类原型。
优点:效率高,只调用了一次
function SuperType(name) {
this.name = name;
this.colors = ["red", "green", "blue"];
}
SuperType.prototype.sayName = function () {
console.log(this.name);
}
function SubType(name, age) {
SuperType.call(this, name);
this.age = age;
}
SubType.prototype = Object.create(SuperType.prototype);
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function () {
console.log(this.age);
};
let instance1 = new SubType("Yooo", 29);
instance1.colors.push("black");
console.log(instance1.colors);//['red', 'green', 'blue', 'black']
instance1.sayName();//Yooo
instance1.sayAge();//29
4)extends
寄生组合继承的语法糖。子类只要继承父类,可以不写 constructor ,一旦写了,则在 constructor 中的第一句话必须是super。
class Son extends Father {
// Son.prototype.__proto__ = Father.prototype
constructor(y) {
super(200) // super(200) => Father.call(this,200)
this.y = y
}
}
async/await 是promise 的语法糖,可以让编写 promise 更清晰易懂。async/await 本质还是promise,使用更清晰的写法来替换 promise.then/catch 的方式。
1)async&await
async:在函数前加上async,函数将返回promise,我们就可以像使用标准Promise一样使用了。
await:await 用于替代 then 使编码更优雅,必须放在 async 定义的函数中使用,一般await后面是外部其它的promise对象。
async function hd(message) {
return new Promise(resolve => {
setTimeout(() => {
resolve(message);
}, 2000);
});
}
async function run() {
let h1 = await hd("Yooo");
console.log(h1);
let h2 = await hd("Alison");
console.log(h2);
}
run();
2)类中使用
和 promise 一样,await 也可以操作thenables 对象
class User {
constructor(name) {
this.name = name;
}
then(resolve, reject) {
let user = ajax(`http://localhost:8888/php/user.php?name=${
this.name}`);
resolve(user);
}
}
async function get() {
let user = await new User("Yooo");
console.log(user);
}
get();
类方法也可以通过 async 与 await 来操作promise
class User {
constructor() {
}
async get(name) {
let user = await ajax(
`http://localhost:8888/php/user.php?name=${
name}`
);
user.name += "-Yooo.com";
return user;
}
}
new User().get("Yooo").then(resolve => {
console.log(resolve);
});
3)错误处理
async 内部发生的错误,会将promise对象变为rejected 状态,所以可以使用catch 来处理。
async function hd() {
console.log(houdunren);
}
hd().catch(error => {
throw new Error(error);
});
如果promise 被拒绝将抛出异常,可以使用 try…catch 处理错误。
async function get(name) {
try {
let user = await ajax(
`http://localhost:8888/php/user.php?name=${
name}`
);
console.log(user);
} catch (error) {
alert("用户不存在");
}
}
get("向军老师");
多个 await 时当前面的出现失败,后面的将不可以执行。
4)并发执行
有时需要多个await 同时执行,有以下几种方法处理。
使用 Promise.all() 处理多个promise并行执行。
async function hd() {
await Promise.all([p1(), p2()]);
}
hd();
让promise先执行后再使用await处理结果
async function hd() {
let h1 = p1();
let h2 = p2();
await h1;
await h2;
}
hd();
js预编译的时候会出现变量函数提升。
函数提升优先级高于变量提升,且不会被同名变量声明时覆盖,但是会被变量赋值后覆盖。
1)变量提升
ES6之前,函数没有块级作用域(一对{}即一个块级作用域),只有全局作用域和函数作用域。变量提升是指将变量声明提升到它所在的作用域的最开始部分。
console.log(foo); // undefined
var foo = '小花猫';
console.log(foo) // 小花猫
相当于
var foo;
console.log(foo);
foo = '小花猫';
console.log(foo);
2)函数提升
函数创建有两种方式:
1、函数声明形式;function foo(){}
2、函数字面量形式(即函数表达式)。var foo = function(){}
(还有一种是方式:函数构造法:var a = new Fun(),技术角度来讲也是一个字面量形式。)
【而只有函数声明形式才有函数提升】
console.log(bar); // f bar() { console.log(123) }
bar(); // 123
var bar = 456;
function bar() {
console.log(123);
}
console.log(bar); // 456
bar = 789;
console.log(bar); // 789
bar(); // bar is not a function
相当于
// 函数提升,函数提升优先级高于变量提升
var bar = function () {
console.log(123)
};
// 变量提升,变量提升不会覆盖(同名)函数提升,只有变量再次赋值时,才会被覆盖
var bar;
console.log(bar); // f bar() { console.log(123) }
bar(); //123
// 变量赋值,覆盖同名函数字面量
bar = 456;
console.log(bar); //456
// 再次赋值
bar = 789
console.log(bar); //789
bar(); // bar is not a function,因为被同名变量覆盖了
console.log(a);
let a = 1;
//报错:在初始化之前无法访问“a”
在作用域中let、const关键字声明的变量会先被创建出来,但是因为此时没有进行词法绑定(就是对声明语句进行求值运算),所以无法被访问,访问就会抛出错误。所以在变量在作用域中被创建到变量可以被访问这段时间,就称为TDZ暂时性死区。
闭包指子函数可以访问外部作用域变量的函数特性。js中所有函数都是闭包,闭包一般在子函数本身作用域以外执行,即延伸作用域。
//使用闭包返回数组区间元素
let arr = [3, 2, 4, 1, 5, 6];
function between(a, b) {
return function(v) {
return v >= a && v <= b;
};
}
console.log(arr.filter(between(3, 5)));
作用:
应用:
问题:
<body>
<div desc="houdunren">在线学习</div>
<div desc="hdcms">开源产品</div>
</body>
<script>
let divs = document.querySelectorAll("div");
divs.forEach(function(item) {
item.addEventListener("click", function() {
console.log(item.getAttribute("desc"));
});
});
</script>
上面item会多次创建,占用内存,最终导致内存泄漏。
通过清除每次循环中item解决内存泄漏问题。
let divs = document.querySelectorAll("div");
divs.forEach(function(item) {
let desc = item.getAttribute("desc");
item.addEventListener("click", function() {
console.log(desc);
});
item = null;
});
let hd = {
user: "后盾人",
get: function() {
return function() {
return this.user;
};
}
};
console.log(hd.get()()); //undefined
使用箭头函数解决这个问题
let hd = {
user: "后盾人",
get: function() {
return () => this.user;
}
};
console.log(hd.get()()); //后盾人
刚学完Html5,为了加深对Html5的理解,便突发奇想的使用纯Html写了一个纯静态的页面,代码通俗易懂,适合小白进行研究学习。代码效果:代码index.html在这里插入代码片```<!DOCTYPE html><html><head><meta charset="UTF-8"><title>MUSIC AND MOV...
UTF-8 and Unicode FAQ for Unix/Linuxby Markus Kuhn This text is a very comprehensive one-stop information resource on how you can use Unicode/UTF-8 on POSIX systems (Linux, Unix). You will find he
记录一些有意思的题目目录索引双色块flag_universe3-11互相伤害!!!Miscellaneous-300intoUJust-No-OneDiskpicture2MulTzor未完待续...双色块下载附件解压出来是个gif文件,仔细观察一下,发现两种色块应该分别代表01。写段Python脚本把数据提取出来from PIL import Image# 读取图片image = Image.open('C:\\Users\\28919\\Desktop\\out.gif')# 存放分离出的
1、将以下文本粘贴到文本文件中,保存为ULPS_Disable.reg;Windows Registry Editor Version 5.00[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Class\{4D36E968-E325-11CE-BFC1-08002BE10318}\0000]"EnableULPS"=dword:00000000;www.xitonghe.com[HKEY_LOCAL_MACHINE\SYSTEM\Cont
解决问题的关键在于版本匹配,compileSdkVersion compileSdkVersion targetSdkVersion这三个参数的整数值都应该是一样的,这里我全部设置成23,在这之前我还升级了:appcompat到com.android.support:appcompat-v7:24.0.0最终编译成功,剩下的问题都是代码问题了a...
空间关系的概念化(中) 上文说的两种空间关系概念化虽然是最常用,但是总给人一种简单粗暴的感觉,所以业界和学术界由搞出了各种各样的空间关系概念化的模型。 首先,就是把两种最简单的概念化给组合起来了,就是下面这种所谓的“无差别区域”法。 无差别的区域(Zone of indifference) 这个名词和翻译,总是让人感觉到怪怪的,但是实际上确很简单,其的意思就是“在一
在利用springMVC开发项目的时候,遇到如下问题:java.lang.IllegalStateException: BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContextat org.springframework.c
一般软件都有开源替代,好不好用这个个人觉得,一般情况下将就下还是可以的。我最近用了不少开源工具,有开发的,有一般应用的,不忍独享,介绍给大家。
一、简介 在现实的机器学习任务中,我们往往是利用搜集到的尽可能多的样本集来输入算法进行训练,以尽可能高的精度为目标,但这里便出现一个问题,一是很多情况下我们不能说搜集到的样本集就能代表真实的全体,其分布也不一定就与真实的全体相同,但是有一点很明确,样本集数量越大则其接近真实全体的可能性也就越大;二是很多算法容易发生过拟合(overfitting),即其过度学习到训练集中一些比较特别的...
方法一:利用利用xlrd读取excel文件其实整个过程比较简单,利用xlrd读取excel文件,再把读取到的数据转换为dict即可。1.安装 xlrdpip install xlrd2.读取文件,并进行格式转换导入的excel表格的格式是这样的:解析后的格式为 [{'编号': 1, '时间': '1988-07-21 00:00:00', '年龄': 1, '分数': 63.2, '总分': 1}, {'编号': 2, '时间': '1988-07-21 00:00:00', '年
http://blog.chinaunix.net/uid-28458801-id-3501888.html当前使用的操作系统 : ubuntu11.10下载jrtplib-3.9.1:http://research.edm.uhasselt.be/~jori/page/index.php?n=CS.Jrtplib1,安装 jrtplib-3.9.1
糊里糊涂用Anaconda安装了pytorch1.5一番,在运行项目的时候还是多多少少有些问题,决定使用pip直接安装试试而不使用功能conda。准备工作确定ubuntu系统的信息:lsb_release -aUbuntu 16.04.6 LTSnvidia-smiCUDA Version: 10.2 查看ubuntu系统的Python版本:python2 --versionpython3 --versionPython 2.7.12Python 3.6.8...