目录

  • 1_js数据类型
  • 2_Object.defineProperty,Object.defineProperties
  • 3_判断空对象
  • 4_判断数组
  • 5_ES6常用语法
  • 6_原型详述
  • 7_继承
  • 8_DOM API
  • 9_BOM_API
  • 10_事件


1_js数据类型

  1. 基本数据类型
    string number boolean undefined null symbol bigint
  2. typeof 和 isNaN
  1. typeof 能判断除了null的基本类型 typeof null === ‘object’,typeof不能判断数字是否为NaN(typeof NaN===‘number’)
  2. typeof能判断声明的函数 typeof function(){} === ‘function’
  3. isNaN(NaN) === isNaN(undefined) === !isNaN(null) === true
  1. instanceof
    手写实现_instanceof(a,b)的时候,注意几个细节
    ① b是否有prototype
    ② a是否为基本数据类型(a==null || typeof a!==‘object’),当变量为基础数据类型的string/number/boolean时,任何尝试访问类实例方法或属性的操作(无论是a.__proto__还是Object.getPrototypeOf(a)),都会在运行时自动创建包装类,即new String/Number/Boolean(a),再访问
    ③ while中比较时,注意return的条件是 a为空或a===b.prototype
  2. 常见引用类型
    Object,Function,Array,Date
  3. 变量比较规则
  1. 相同类型的基本类型比较(也叫不可变的值的比较)
Symbol有一点点小小的特殊,有,但不是完全有
其他的,值相等且类型一样 === 就返回true

Symbol.for(key) //注意 作为key 1==='1', Symbol.for('1') === Symbol.for(1) //true
从全局Symbol注册表中找key,找得到就返回那个Symbol,找不到再创建

Symbol(sth)
每次调用都会创建一个独一无二的Symbol //Symbol(1) == Symbol(1) //false
  1. 引用类型的比较(也叫可变值的比较)
==和===都是比较引用是否相同
例:
x = new Number(1)
y = new Number(1)
x==y //false

x = {toString:function(){return 99}}
y = new Date();y.toString=function(){return 99}
z = function(){};z.toString=function(){return 99}
x==99,y==99,z==99 //true
x==y,x==z,y==z //false
(引用类型,即使是对象和函数和日期类,但是都是Object,所以不是不同类型的比较)
  1. 不同类型的比较
// === 一定返回false
1.NaN与所有的值都不等,包括自己 NaN == NaN //false

2.
	null == undefined //true
	null === null //true

3.引用类型和字符串比较 等同于 obj.toString() === str
	({}) == '[object Object]' //true
	var date = new Date()
	[date] == date.toString() //true
    [1,3] == '1,3' //true
    
    function f(){}
	f.toString=function(){return 1}
	f == 1 //true
4. Number的实例与其它类型比较,隐式转换调用的是valueOf,而不是toString
例:	
x = new Number(1)
x.toString=function(){return 'haha'}
x=='haha' //false
x.valueOf=function(){return 'vvvv'}
x=='vvvv' //true

5.其他的!**不同**!类型比较,都是先转为数字再比较
(1) 对象->字符串->数字 即 Number(obj.toString()) 比如[]==0,[].toString()->'',''->0, 0==0 //true
(2) 字符串->数字
(3) 布尔->数字(1/0)
(4) undefined->NaN (也就是 undefined==null,undefined===undefined)
(5) Symbol cannot convert to Number,所以Symbol与万物不等(Symbol.for()不是例外,只是创建机制不一样)

2_Object.defineProperty,Object.defineProperties

  1. Object.defineProperty(obj,key||Symbol,descriptor)
    注意:Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。合理的做法是在Object的构造函数中使用。
//descriptor:
{
    configurable, //定义为false后,不能再进行任何配置,也不能重新设置为true
    enumerable,   //是否可在for in和 Object.keys中枚举
    writable,     //value是否可修改,严格模式下修改会报错
    value,        //显示定义这个值
}
    
var x = {a:'a'}
Object.defineProperty(x,'a',{
    enumerable:false
})
for(let k in x){console.log(k)} //nothing

getter和setter

x = {__name:'xiao li'}
Object.defineProperty(x,'name',{
    get:function(){
        return 'i am ' + this.__name
    },
    set:function(name){
        this.__name = 'xiao ' + name
    }
})

console.log(x.name) //i am xiao li
x.name = 'zhang'
console.log(x.name) //i am xiao zhang
  1. Object.defineProperties
var x = {a:'a',b:'b'}
Object.defineProperties(x,{
    a:{
        enumerable:false
    },
    b:{
        writable: false
    }
})

x.b='c'
for(let k in x){console.log(x[k])} //'b'

3_判断空对象

JSON.stringify(x) === '{}' //如果x包含函数,那么这种方式无效
x = {f:function(){}}
console.log(JSON.stringify(x) === '{}') //true

x = {a:'a'}
Object.defineProperty(x,'a',{enumerable:false})
console.log(Object.keys(x).length===0) //这个api返回的是enumerable:true的keys

Object.getOwnPropertyNames(x) == 0 //[]==0 为true
//这种方法也有缺陷,它能返回enumerable为false的keys,但是不能返回使用Symbol部署的属性
x = {a:'a'}
Object.defineProperty(x,'a',{enumerable:false})
console.log(Object.getOwnPropertyNames(x)) //['a']
//BUT
x = {}
x[Symbol.for(1)] = 1
x[Symbol.iterator] = function(){}
console.log(Object.getOwnPropertyNames(x)) //[]

//比较通用的方案
function isEmptyObject(x){
    //是否为基本数据类型
    if(x==null || typeof x!=='object'){
        return false
    }
    return Object.getOwnPropertyNames(x).length==0 && Object.getOwnPropertySymbols(x).length == 0
}

4_判断数组

var x = {0:'ele_0',length:0}
var arr = [1,2,3]

//1. Array.isArray
console.log(Array.isArray(x))
console.log(Array.isArray(arr))
//2. arr.constructor === Array
//3. arr instanceof Array
//4. Object.prototype.toString.call(arr) === '[object Array]'

5_ES6常用语法

  1. var let const
  1. 块级作用域、暂时性死区、变量地址不可变
  1. 模板字符串
  2. 解构赋值(对象、数组解构,重命名)
  3. 扩展运算符…(解构、rest参数、数组或对象部分属性复制、类数组转换)
  4. 部署了Symbol.iterator的原生数据结构及for of和…
    string,map,set,array,类数组(arguments,nodelist等)
  5. 箭头函数(注意this,不能用作构造函数,bind对它的影响,使用rest代替arguments)
  6. Promise,async/await,generator/yield(Promise链式调用、Promise api原理)
  7. import/export

6_原型详述

(1)每个函数有一个prototype指向其原型对象

(2)每个构造函数的实例对象有一个__proto__指向其构造函数的原型对象

(3)原型对象的constructor指向其构造函数

(4)访问对象的一个属性,会顺着原型链往上找,直到找到Object的原型对象上还没有,就返回undefined

(5)Object的原型对象的__proto__指向null

(6)Object.create(null)创造的对象身上无任何属性 x instanceof Object //false

(7)在使用构造函数new一个实例对象时,首先使用Object.create(Father.prototype)生成一个空对象(空对象的__proto__指向父类的prototype),然后让this指向这个空对象,然后执行构造函数内部代码,最后如果没有主动return object,就return this(return null也被忽略,仍然返回this)。

(8)一个构造函数new的所有实例的__proto__都指向同一个原型对象

7_继承

(0)父亲

function Father(name){
    console.log('执行父类构造函数')
    this.name = name
    this.sayName = function(){
        alert(this.name)
    }
}

Father.prototype.age = 10

(1)原型链继承

function Person(){
    
}
Person.prototype = new Father() //Person new的实例的构造函数会指向Father,所以要用下面一行代码修复
Person.prototype.constructor = Person
var p = new Person('xxx') //p.__proto__ === Person.prototype, p.__proto__.__proto__ === Father.prototype
console.log(p.age) //10
console.log(p.name) //undefined

重点:让新实例的原型等于父类的实例。
    特点:1、实例可继承父类构造函数内this挂载的属性及方法,可继承父类原型对象上的方法
    缺点:1、(重点)新实例无法向父类构造函数传参。
       2、继承单一。
       3、所有新实例都会共享父类实例和原型对象上的属性。

(2)借用构造函数继承

function Person(name){
    Father.call(this,name)
}
var p = new Person('xxx')
console.log(p.name) //xxx
p.sayName() //xxx
console.log(p.age) //undefined

重点:用.call()和.apply()将父类构造函数引入子类函数(在子类函数中做了父类函数的自执行(复制))
    特点:1、只继承了父类构造函数的属性,没有继承父类原型的属性。
       2、解决了原型链继承缺点1、2、3。
       3、可以继承多个构造函数属性(call多个)。
       4、在子实例中可向父实例传参。
    缺点:1、只能继承父类构造函数的属性。
       2、每次new都要重新调用父类构造函数
       3、每个新实例都有父类构造函数的副本,臃肿。

(3)组合继承(原型链继承+借用构造函数继承)

function Person(name){
    Father.call(this,name)
}
Person.prototype = new Father() //Person new的实例的构造函数会指向Father,所以要用下面一行代码修复
Person.prototype.constructor = Person
var p = new Person('xxx')
console.log(p.name) //xxx
console.log(p.age) //10
p.sayName() //xxx

重点:结合了两种模式的优点,传参和复用
    特点:1、可以继承父类原型上的属性,可以传参,可复用。
       2、每个新实例引入的构造函数属性是私有的。
    缺点:调用了两次父类构造函数(耗内存),子类的构造函数会代替原型上的那个父类构造函数(如果主动修复的话,但是逻辑上是应当修复的)。

(4)原型式继承/实例继承

function Person(name){
    let instance = new Father()
    instance.name = name
    return instance
}
var p = new Person('xxx') //也可以直接var p = Person()
console.log(p.name,p.age,p.sayName) //xxx,10,f
console.log(p instanceof Person) //false

优点:

不限制调用方式

简单,易实现

缺点:

不能多次继承

(5)寄生组合继承

function Person(name){
    //继承父类构造器属性
    Father.call(this,name)
}

//继承父类原型对象上的属性:写法一
(function(){
    //创建空类
    let Super = function(){}
    Super.prototype = Father.prototype
    //父类的实例作为子类的原型
    Person.prototype = new Super() //Person.prototype.__proto__ === Father.prototype
}
)()
//修复构造函数指向
Person.prototype.constructor = Person

//继承父类原型对象上的属性:写法二
(function(){
    var prototype = Object.create(Father.prototype) //function F(){};F.prototype = param;return new F();
    //prototype.__proto__=Father.prototype
    prototype.constructor = Person
    Person.prototype = prototype
})()

var p = new Person('xxx')
console.log(p.name,p.sayName,p.age)//xxx f 10

PS:

constructor其实没有什么用处,只是JavaScript语言设计的历史遗留物。由于constructor属性是可以变更的,所以未必真的指向对象的构造函数,只是一个提示。实际中,除非通过new xxx._proto_.constructor()显示调用,否则没有影响。

不过,从编程习惯上,我们应该尽量让对象的constructor指向其构造函数,以维持这个惯例。

(6)ES6 class extends

class A{
    a(){}
    b=()=>{}
    static c = ()=>{}
}

class B extends A{}
//(1)B.__proto__===A
//(2)B.prototype.__proto__ === A.prototype,...A.call(this,...params)
//(1)导致B也继承了A的静态方法
//(2)就是常规的原型链和构造函数的组合继承,既能继承构造函数的属性,也能继承A的原型上的属性

8_DOM API

单独的一篇文章整理。或者可以移步知乎,这篇讲的很好。

https://zhuanlan.zhihu.com/p/130298762

9_BOM_API

  1. 什么是BOM?浏览器对象模型
  2. 常用的BOM属性?
  1. location
  1. href 返回或设置当前文档的url
  2. search 返回查询字符串的部分,包括"?"
  3. hash 返回url #后的内容
  4. host 返回域名
  5. pathname 域名后的部分
  6. protocal 协议(包括冒号)
  7. port 端口(显示访问的才有)
  8. reload() 重载页面
  9. replace(url)设置当前页面url并删除当前页面的历史记录
  1. history
  1. go() //go()刷新,go(1)前进一个,go(-1)回退一个
  2. forward() 前进
  3. H5 pushState react-router-dom的browserrouter使用的是这个api
  1. navigator
  1. userAgent 用于判断用户的浏览器,IOS或Android

10_事件

  1. 事件流
    页面接收事件的顺序,一种是事件冒泡,一种是事件捕获,先进入捕获阶段,从外层(document)到内层,再到处于目标阶段,再到冒泡阶段。
  2. 绑定事件的方法
  1. DOM 0级:
//绑定 - 只能绑定一个
btn.onclick = function(e){}
//删除
btn.onclick = null
  1. DOM 2级:
//绑定 - 能绑定多个回调函数
var callbackFn1 = function(e){}
btn.addEventListener('click',callbackFn1)
//移除
btn.removeEventListener('click',callbackFn1)
  1. 事件委托
    场景:需要为大量dom元素添加时间处理元素的时候,比如为ul的li们添加点击监听事件,如果为每一个li都绑定事件,非常消耗资源。事件委托就是把对子元素的监听,委托给外层元素,利用事件冒泡,由外层元素的回调函数统一处理
//ul>li{$}*5
document.querySelector('ul').onclick = function(e){
    //e.currentTarget===this===绑定事件的元素
    console.log(e.target.textContent) //e.target是点击的li
}