不能使用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]’ //truearr instanceof Array // true首先,定义一个父类
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特点:
结合原型链方法和构造函数方法缺点:
调用两次父类的构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)首先,Promise中的执行函数是同步进行的,但是里面存在着异步操作
在异步操作结束后,会调用resolve或reject方法,这两者方法都是作为微任务进入到EventLoop中
处理回调问题的方式:
使用同步回调
异步任务处理会有一定时间延迟,如果异步操作后使用同步回调,则会让整个脚本阻塞,后面的任务都无法得到执行
等待的时间无法完成其他事情,导致CPU利用率低
使用异步回调,将回调函数放在宏任务队列的队尾
异步回调能够解决后续任务的阻塞问题
执行回调(resolve/reject)的时机应该是在前面所有的宏任务完成之后,倘若现在的任务队列非常长,那么回调迟迟得不到执行,造成应用卡顿
Promise利用微任务解决了两大痛点
Promise 引入微任务, 即把 resolve(reject) 回调的执行放在当前宏任务的末尾
采用异步回调替代同步回调,解决浪费CPU性能的问题将任务放到当前宏任务最后执行,解决了回调执行的实时性问题在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能够知道页面中的按键操作
null和undefined都是js中的基本数据类型
typeof null // Objecttypeof undefined // undefinednull == undefined // truenull === undefined // false关于3和4:两等于表示不全等,主要比较数值是否相等,在进行比较时会进行类型转换;而三等于表示严格相等,不仅数值要相同,数据类型也要相同
但null和undefined在进行比较时,不会进行数据类型的转换,但是在不全等中判断null和undefined相等是由于两者的行为很相似,都表示一个无效的值
null :表示无值undefined : 表示一个未声明的变量,或已声明但没有赋值的变量,或一个并不存在的对象属性在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}事件委托机制是一种代理模式,其基于事件冒泡,子元素上的事件对象会冒泡给父元素,父元素也会触发其相应的事件
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等,不适合事件委托基本用法:
Object.defineProperty(obj, 'propertyName', { value: 42, writable: false })Object.defineProperty()方法用于直接在一个对象上定义一个新属性,或者修改一个对象的现有属性
参数1 obj:目标对象propertyName:要定义或修改的属性名称descriptor:要定义或修改的属性的描述符,或者称作选项配置常见配置:
configurable:其值为true时(默认为true),该属性的描述符才能被改变,同时该属性也能从对应的对象上被删除enumerable:表示属性是否能被枚举/遍历value:属性的值writable:表示属性值是否可以被赋值运算符修改get:读取属性时,调用getter函数set:设置属性时,调用setter函数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(); // xfbind的另一种用法
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
|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() |把数组转换为字符串,并返回结果。 |
严格模式,即在严格的条件下运行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
但是存在一个问题:这两种方法都不能将属性中Symbol类型的属性给返回出来,详情看下面的Symbol
基本数据类型:String,Number,Boolean,null,undefined,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 值必须放在方括号之中,如果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)]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与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
