js常见用法
1. 判断一个对象是否为数组
不能使用typeof关键字
let arr = [1, 2, 3];
let obj1 = {name: 'xf'};
let obj2 = null;
//打印出的结果都是 object
console.log(typeof arr);
console.log(typeof obj1);
console.log(typeof obj2);
- Array.isArray(arr) //true
- 判断原型
let arr = [1, 2, 3]; console.log(arr.__proto__ === Array.prototype) //true
- Object.prototype.toString.call(arr) === ‘[object Array]’ //true
- arr instanceof Array // true
2. js继承的实现方式
首先,定义一个父类
function Animal(name){
this.name = name;
this.sleep = function() {
console.log(this.name + '正在睡觉');
}
}
Animal.prototype.eat = function() {
console.log(this.name + '正在吃东西');
}
原型链继承
核心任务是完成原型链上的对接
function Cat(name){
this.name = name
}
Cat.prototype = new Animal();
// 完善原型链上的工作
Cat.prototype.constructor = Cat;
let cat = new Cat('xiefeng');
console.log(cat.name); //xiefeng
console.log(cat.eat()); //xiefeng正在吃东西
console.log(cat.sleep()); //谢锋正在睡觉
实现核心:完成原型链的对接,子类的原型对象为父类的实例
特点:
- cat是子类的实例,也是父类的实例
- 父类新增原型方法或属性,子类可以访问
- 简单,易于实现
缺点:
- 无法实现多继承
构造函数继承
使用父类的构造函数来增强子类实例,该方法没有完成原型链上的对接
function Cat(name){
Animal.call(this);
this.name = name
}
let cat = new Cat('xiefeng');
console.log(cat.name); //xiefeng
console.log(cat instanceof Animal); //false
使用Animal.call(this)动态改变this指向,并且在call语句中可以进行传参
特点:
- 创建子类实例时,可以向父类传递参数
- 可以实现多继承,即call多个对象
缺点:
- 实例不再是Animal(父类)的实例,只是子类的实例
- 只能继承父类的实例的属性和方法,不能继承原型的属性和方法
组合继承
核心:通过调用父类构造函数,继承父类的属性并保留传参的优点,同时通过将父类实例作为子类原型,实现原型链继承
function Cat(name){
Animal.call(this, name)
}
Cat.prototype = new Animal();
// 完善原型链上的工作
Cat.prototype.constructor = Cat;
let cat = new Cat('xiefeng');
console.log(cat.name); //xiefeng
console.log(cat instanceof Cat); //true
console.log(cat instanceof Animal); //true
特点:
- 结合原型链方法和构造函数方法
缺点:
- 调用两次父类的构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)
寄生组合继承
function Cat(name){
Animal.call(this, name);
}
// Object.create():用于创建对象,并为该对象选择原型对象
Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;
let cat = new Cat('xiefeng');
圣杯继承方式
function Father(){}
function Son(){}
Father.prototype.name = 'Xie';
//圣杯模式
function inherit(Son, Father) {
function F(){} //中间变量F
// 让F和Father的原型指向同一对象
F.prototype = Father.prototype;
// 用中间变量的实例作为子类的原型对象,如果修改子类原型对象中的方法或属性,则对父类没有影响
Son.prototype = new F();
//由于Target.prototype指向的是objF,因此并没有constructor这一属性,沿着__proto__向上查找,发现constructor指向的是Father,
//因此这里可以进行归位,让它的constructor重新指向它自己
Son.prototype.constructor = Son;
//uber是超类的意思,这里主要用来储存这个目标到底继承自谁,可写可不写
Son.prototype.uber = Father;
}
inherit(Father, Son);
Son.prototype.sex='male';
console.log(son.lastName);//Jack
console.log(son.sex);//male
// 对Father的原型对象不会产生任何影响
console.log(father.sex);//undefined
3. 为什么Promise引入了微任务
首先,Promise中的执行函数是同步进行的,但是里面存在着异步操作
在异步操作结束后,会调用resolve或reject方法,这两者方法都是作为微任务进入到EventLoop中
处理回调问题的方式:
-
使用同步回调
异步任务处理会有一定时间延迟,如果异步操作后使用同步回调,则会让整个脚本阻塞,后面的任务都无法得到执行
等待的时间无法完成其他事情,导致CPU利用率低
-
使用异步回调,将回调函数放在宏任务队列的队尾
异步回调能够解决后续任务的阻塞问题
执行回调(resolve/reject)的时机应该是在前面所有的宏任务完成之后,倘若现在的任务队列非常长,那么回调迟迟得不到执行,造成应用卡顿
Promise利用微任务解决了两大痛点
Promise 引入微任务, 即把 resolve(reject) 回调的执行放在当前宏任务的末尾
- 采用异步回调替代同步回调,解决浪费CPU性能的问题
- 将任务放到当前宏任务最后执行,解决了回调执行的实时性问题
4. 如何获取页面上的事件对象
e = event || window.event
在IE/Opera中,使用window.event,而在FireFox中,是event
event || window.event应用
function testKeyDown(event){
event = event || window.event;
if(event.KeyCode == 13){
alert("回车键");
}
if(event.shiftKey == true){
alert("shift键");
}
}
通过KeyCode能够知道页面中的按键操作
5. null和undefined
null和undefined都是js中的基本数据类型
- typeof null // Object
- typeof undefined // undefined
- null == undefined // true
- null === undefined // false
关于3和4:两等于表示不全等,主要比较数值是否相等,在进行比较时会进行类型转换;而三等于表示严格相等,不仅数值要相同,数据类型也要相同
但null和undefined在进行比较时,不会进行数据类型的转换,但是在不全等中判断null和undefined相等是由于两者的行为很相似,都表示一个无效的值
- null :表示无值
- undefined : 表示一个未声明的变量,或已声明但没有赋值的变量,或一个并不存在的对象属性
6. 箭头函数和普通函数的区别
在ES6中,提供=>来定义一个箭头函数,箭头函数里包裹操作语句
箭头函数和普通函数有着很大的区别
-
箭头函数不是一个构造器(constructor),同时也不能使用new关键字来实例化对象
-
箭头函数没有this指针,箭头函数内部的this指针只能通过作用域链向上寻找确定
-
箭头函数内部没有arguments对象
-
可以进行函数简写
-
如果箭头函数没有参数,直接写一个空括号即可。
-
如果箭头函数的参数只有一个,也可以省去包裹参数的括号。
-
如果箭头函数有多个参数,将参数依次用逗号(,)分隔,包裹在括号中即可
let a = (x) => x; //这样可以省略return let b = (x) => {x;} //{}会被认为是函数体的大括号 let c = (x) => {{x;}} console.log(a(1), b(1), c(1)); // 1 undefined undefined let d = x => ({x}); console.log(d(1)); //{x: 1}
-
7. 事件委托机制
事件委托机制是一种代理模式,其基于事件冒泡,子元素上的事件对象会冒泡给父元素,父元素也会触发其相应的事件
let ul = document.getElementById("ul");
ul.addEventListener("click", function(ev){
ev = ev || window.event;
let target = e.target || e.srcElement;
if(target.nodeName.toUpperCase() === "LI"){
target.style.backgroundColor = "red";
}
})
事件代理的优缺点:
- 优点:事件代理可以减少子元素的事件注册,只需要在父级元素上对事件进行注册即可;可以实现动态新增子元素,并且新增的子元素也无需进行事件绑定
- 缺点:无法冒泡的事件无法使用事件代理,比如blur/focus等;频繁触发的事件如 mousemove、mouseout、mouseover等,不适合事件委托
8. 数组升序/降序
let arr = [4, 1, 3, 2, 5];
arr.sort((a, b)=>a-b); // [1, 2, 3, 4, 5]升序结果
arr.sort((a, b)=>b-a); // [5, 4, 3, 2, 1]降序结果
9. Object.defineProperty
基本用法:
Object.defineProperty(obj, 'propertyName', {
value: 42,
writable: false
})
Object.defineProperty()方法用于直接在一个对象上定义一个新属性,或者修改一个对象的现有属性
- 参数1 obj:目标对象
- propertyName:要定义或修改的属性名称
- descriptor:要定义或修改的属性的描述符,或者称作选项配置
常见配置:
- configurable:其值为true时(默认为true),该属性的描述符才能被改变,同时该属性也能从对应的对象上被删除
- enumerable:表示属性是否能被枚举/遍历
- value:属性的值
- writable:表示属性值是否可以被赋值运算符修改
- get:读取属性时,调用getter函数
- set:设置属性时,调用setter函数
10. bind()
bind方法是将上下文对象绑定给一个函数并返回对应函数,便于稍后调用
apply和call方法则是改变this指向,并立即调用
bind,apply,call三个方法都可以改变函数运行时的上下文对象(context)
Function.prototype.bind()
方法主要将函数绑定到某个对象,该方法会返回一个函数,函数体内的this对象的值会被绑定到传入bind()第一个参数的值
let a = {
name: "xf",
b(){
let c = function(){
console.log(this.name);
}
c(); //由于是一个普通函数调用,this为window
}
}
a.b(); // undefined
第一个解决办法就是保存this指向
let a = {
name: "xf",
b(){
let that = this;
let c = function(){
console.log(that.name);
}
c();
}
}
a.b(); // xf
另一种解决办法就是使用bind方法
let a = {
name: "xf",
b(){
let c = function(){
console.log(this.name);
}.bind(this);
c();
// 也可以是: c().bind(this)()
}
}
a.b(); // xf
bind的另一种用法
function f(y, z){
return this.x + y + z;
}
var m = f.bind({x : 1}, 2);
console.log(m(3));
//6
这里bind方法会把它的第一个实参绑定给f函数体内的this,所以这里的this即指向{x : 1}对象,从第二个参数起,会依次传递给原始函数,这里的第二个参数2,即是f函数的y参数,最后调用m(3)的时候,这里的3便是最后一个参数z了,所以执行结果为1 + 2 + 3 = 6
11. js String常用方法
- substr(start, length)
- substring(from, to)
- slice(from, to)
- split(“seperator”); 切分
- trim() 去除字符串两边的空白
- indexOf, lastIndexOf
- includes 查找字符串中是否包含指定的子字符串
- charAt() 方法可返回指定位置的字符
12. Array常用方法
|concat() |连接两个或更多的数组,并返回结果。 |
|entries() |返回数组的可迭代对象。 |
|keys() |返回数组的可迭代对象,包含原始数组的键(key)。 |
|every() |检测数值元素的每个元素是否都符合条件。 |
|some() |检测数组元素中是否有元素符合指定条件。 |
|array.fill(value, start, end) |使用一个固定值来填充数组。 |
|filter() |检测数值元素,并返回符合条件所有元素的数组。 |
|map() |通过指定函数处理数组的每个元素,并返回处理后的数组。 |
|forEach() |数组每个元素都执行一次回调函数。 |
|reduce() |将数组元素计算为一个值(从左到右)。 |
|reduceRight() |将数组元素计算为一个值(从右到左)。 |
|find() |返回符合传入测试(函数)条件的数组第一个元素的值。 |
|findIndex() |返回符合传入测试(函数)条件的数组元素索引。 |
|indexOf() |搜索数组中的元素,并返回它所在的位置。 |
|lastIndexOf() |搜索数组中的元素,并返回它最后出现的位置。 |
|Array.from |通过给定的对象中创建一个数组。 |
|includes() |判断一个数组是否包含一个指定的值。 |
|isArray() |判断对象是否为数组。 |
|join() |把数组的所有元素放入一个字符串,默认为,号连接。 |
|reverse() |反转数组的元素顺序。 |
|pop() |删除数组的最后一个元素并返回删除的元素。 |
|push() |向数组的末尾添加一个或更多元素,并返回新的长度。 |
|shift() |删除并返回数组的第一个元素。 |
|unshift() |向数组的开头添加一个或更多元素,并返回新的长度。 |
|slice() |选取数组的的一部分,并返回一个新数组。 |
|sort() |对数组的元素进行排序。 |
|array.splice(index,howmany,item1,…,itemX) |从数组中添加或删除元素(改变数组自身)。 |
|toString() |把数组转换为字符串,并返回结果。 |
13. 严格模式"use strict"
严格模式,即在严格的条件下运行Javascript脚本,严格模式消除了Javascript语法的一些不合理、不严谨之处,减少一些怪异行为
-
不允许直接声明全局变量,只能使用var/let/const关键词申明变量,不允许不使用关键词(即直接)声明变量
-
不允许delete变量和函数
// 严格模式 "use strict"; var x = 1; delete x; // Uncaught SyntaxError: Delete of an unqualified identifier in strict mode.
-
要求函数的参数名唯一
-
arguments保留原始参数
// 非严格模式 function s(a, b){ a = 2; console.log(arguments[0], arguments[1]); // 2 2 } s(1, 2);
// 严格模式 "use strict"; function s(a, b){ a = 2; console.log(arguments[0], arguments[1]); // 1 2 } s(1, 2);
-
this不再绑定为window,而是undefined
14. 判断一个对象是否为空对象
- Object.keys(obj).length === 0
- JSON.stringify(obj) === “{}”
但是存在一个问题:这两种方法都不能将属性中Symbol类型的属性给返回出来,详情看下面的Symbol
15. Symbol
基本数据类型:String,Number,Boolean,null,undefined,Symbol
Symbol的基本介绍
Symbol表示独一无二,通过Symbol()
生成。凡属于Symbol类型都是独一无二的,从而可以保证不会与其他属性名产生命名冲突
let a = Symbol("hello");
// Symbol()函数里面传递的参数只是该Symbol变量的描述
typeof a; //Symbol
a.toString(); //'Symbol(hello)'
a.description;//hello,用于获取Symbol的描述文字
Symbol函数可以接收一个字符串作为参数,表示对Symbol实例的描述,Symbol其实是一种类似于字符串的数据类型
如果Symbol的参数是一个对象,就会调用该对象的toString方法将其转为字符串,然后生成Symbol值
注意:Symbol函数的参数只是表示对当前 Symbol 值的描述,因此相同参数的Symbol函数的返回值是不相等的
let a = Symbol("a");
let aa = Symbol("a");
a === aa; //false
Symbol作为属性名
对象的属性名现在可以有两种类型
- 一种是原来就有的字符串
- 另一种就是新增的 Symbol 类型
凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突
注意:
- Symbol 值作为对象属性名时,不能用点运算符,因为点运算符后面总是字符串
- 在对象的内部,使用 Symbol 值定义属性时,Symbol 值必须放在方括号之中,如果s不放在方括号中,该属性的键名就是字符串s,而不是s所代表的那个 Symbol 值
let obj = {};
// String型作为属性
obj["name"] = "xf";
obj.age = "21";
console.log(obj); //{name: "xf", age: 21}
//Symbol型作为属性
let gender = Symbol("gender");
obj[gender] = "male"
console.log(obj); //{name: "xf", age: 21, Symbol(gender): "male"}
let mySymbol = Symbol();
// 第一种写法
let a = {};
a[mySymbol] = 'Hello!';
// 第二种写法
let a = {
[mySymbol]: 'Hello!'
};
// 第三种写法
let a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });
关于遍历
Symbol 作为属性名,遍历对象的时候,该属性不会出现在for...in
、for...of
循环中,也不会被Object.keys()
、Object.getOwnPropertyNames()
、JSON.stringify()
返回
有一个Object.getOwnPropertySymbols()
方法,可以获取指定对象的所有 Symbol 属性名。该方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值
let a = Symbol("a");
let obj = {
[a]: "a"
}
JSON.stringify(obj); //{}
Object.getOwnPropertySymbols(obj) //[Symbol(a)]
16. Set & WeakSet, Map & WeakMap
Set
Set和WeakSet都是一种新的数据结构,表示没有重复值的集合
let s = new Set();
let ws = new WeakSet();
Set和WeakSet提供的方法
- add()
- delete()
- has()
- clear():Set独有
- keys()
- values()
- set.forEach((value, key)=>{console.log(key, value)})
Set和WeakSet的区别
-
Set中的成员可以是任意类型,而WeakSet限定了成员只能是对象
-
WeakSet当中的对象成员都是弱引用,即垃圾回收站机制不考虑WeakSet对该对象的引用
WeakSet中的成员可能随时都会消失,即被垃圾回收站给回收
Set去重
[...new Set(arr)]
Map
Map与Object类似,都是生成键值对集合,Object提供了“字符串/Symbol-值”的对应关系,而Map中提供“值-值对应关系”,各种类型的值都可以当作键
const m = new Map();
const obj = {name: "xf"};
m.set(o, "content");
m.get(o); //content
m.has(o); //true
m.delete(o); //true
const map = new Map([
["name": "xf"],
["age", 21]
])
Map和WeakMap的区别
- WeakMap只接收对象作为键名(null除外),不接受其他类型的值作为键
- WeakMap对键名所引用的对象都是弱引用,即垃圾回收机制不将该引用考虑在内
基本上,如果你要往对象上添加数据,又不想干扰垃圾回收机制,就可以使用 WeakMap