JavaScript

227 阅读5分钟

对象

类对象:是对象类型的子类型

  1. 数组(Array)类
  2. 函数(Function)类
  3. 日期(Date)类
  4. 正则(RegExp)类
  5. 错误(Error)类

判断数据类型的方法

  1. typeof 缺点: 无法区分Object和Array等引用数据类型
console.log(typeof 1)
console.log(typeof 'a')
console.log(typeof new Object()) //object
console.log(typeof new Array()) //object
console.log(typeof null) //object
console.log(typeof undefined)  // undefined
  1. instanceof
  • 定义: 判断A是否是B的实例
  • 缺点:
    • 只能判断引用数据类型
    • 无法判断null和undefined
console.log(1 instanceof Number) // false
console.log('a' instanceof String) // false
console.log(new Object() instanceof Object) // true
console.log(new Array() instanceof Array) // true
console.log(null instanceof Object) //false
console.log(undefined instanceof Object) //false
  1. constructor 缺点:无法判断null和undefined
let xx = 1
console.log(xx.constructor === Number) // true
console.log('a'.constructor === String) //true
console.log(new Object().constructor === Object) //true
console.log(new Array().constructor === Array) //true
let x = null
// console.log(x.constructor) //TypeError: Cannot read property 'constructor' of null
let y = undefined
// console.log(y.constructor) //TypeError: Cannot read property 'constructor' of undefined

  1. Object.prototype.toString().call()
console.log(Object.prototype.toString.call(1)) //[object Number]
console.log(Object.prototype.toString.call('a')) //[object String]
console.log(Object.prototype.toString.call(new Object)) //[object Object]
console.log(Object.prototype.toString.call(new Array)) //[object Array]
console.log(Object.prototype.toString.call(null)) //[object Null]
console.log(Object.prototype.toString.call(undefined)) //[object Undefined]

原型和原型指针

  • JavaScript所有对象都有一个__proto__(原型指针)属性;
  • 对象的__proto__(原型指针) 指向 构造函数的prototype(原型);
var obj = new Object()
obj.__proto__ === Object.prototype 

function fun(){}
fun.__proto__ === Function.prototype

var f = new  fun()
f.__proto__ === fun.prototype

fun.prototype.__proto__ === Object.prototype
Function.prototype.__proto__ === Object.prototype
Object.prototype.__proto__ === null

实现继承

作用域和作用域链

  • 作用域的定义
    • 作用域是可访问变量的结合。
    • 在JavaScript中,对象和函数同样也是变量;
    • 在JavaScript中,作用域是可访问变量、对象、函数的集合。
  • 局部作用域
    • 变量在函数内声明,变量为局部作用域;
    • 局部变量可能再函数内部访问;
    • 局部变量在函数开始执行时创建,函数执行后局部变量会自动销毁。
  • 全局作用域
    • 变量在函数外定义,即为全局变量;
    • 全局变量有全局作用域,网页中所有的脚本和函数均可使用;
    • 如果变量在函数中没有声明(没有使用var关键字),该变量为全局变量。
  • 块级作用域
    • ES6之前,没有块级作用域的概念;
    • ES6可以使用let关键字来实现块级作用域;
    • let声明的变量只在let命令所在的代码块{}内有效。
  • 变量的生命周期
    • js变量生命周期在它声明时初始化;
    • 局部变量在函数执行完毕后销毁;
    • 全局变量在页面关闭后销毁。
  • 作用域链
    • 一般情况下,变量是在定义这个变量的函数作用域中取值,但是如果在当前作用域中没有查到值,就会向上级作用域去查,直到查到全局作用域,这么一个查找过程形成的链条就叫做作用域链。
    • 在JavaScript执行过程中,其作用域链是由词法作用域决定的。
    • 词法作用域是由代码在函数声明的位置来决定的,所以词法作用域是静态作用域,通过它能够预测代码在执行过程中如何查找标识符。
        function bar(){
            console.log(myName) // yanessa
        }

        function foo(){
            var myName = "ysg"
            bar()
        }

        var myName = "yanessa"
        foo()

闭包

参考链接: juejin.cn/post/695791…

  • JavaScript除了静态作用域链外,还有一个特点就是函数可以作为返回值。这就导致一个问题,本来按照顺序创建调用一层层函数,按顺序创建和销毁作用域挺好的,但是如果内层函数返回了或者通过别的暴露出去了,那么外层函数销毁,内层函数却没有销毁,这时候该怎么处理作用域,父作用域销不销毁?
  • 针对上叙情况,JavaScript就设计了闭包的机制。
  • 静态作用域链中的父作用域先于子作用域销毁怎么解决?
  • 父作用域要不要销毁?是不是父作用域不销毁就行了?
    • 不行的,父作用域中很多东西和子函数无关,为啥因为子函数没有结束就一直常驻内存,这样肯定有性能问题,所以还是要销毁。但是销毁父作用域不能影响子函数,所以要创建一个对象,把子函数内引用的父作用域的变量打包起来,给子函数打包带走。

DOM事件流

zhuanlan.zhihu.com/p/51611590

  • DOM事件流的三个阶段

    • 捕获阶段
    • 目标阶段
    • 冒泡阶段:事件从目标节点自下而上向window对象传播的阶段。
  • 事件句柄

    事件句柄(又称事件处理函数),是指事件发生时要进行的操作。

  • addEventListener的useCapture

    • useCapture为addEventListener的第三个参数
    • usecapture=true,事件句柄在捕获阶段执行
    • useCapture=false 默认,事件句柄在冒泡阶段执行
<body>
    <div id="father">
        <div id="son">1</div>
    </div>
    <script>
        const father = document.getElementById('father')
        father.addEventListener('click', function(e){
            console.log("点击父元素", e.target)
        }, false)

        const son = document.getElementById('son')
        son.addEventListener('click', function(e){
            console.log("点击子元素", e.target)
        })
    </script>
</body>

如上:当father的click事件的useCapture为false时,输出如下:

点击子元素 <div id="son">1</div>
点击父元素 <div id="son">1</div>

当father的click事件的useCapture为true时,输入如下:

点击父元素 <div id="son">1</div>
点击子元素 <div id="son">1</div>
  • event.preventDefaults()

    如果调用这个方法,默认事件行为将不再触发。

    什么是默认事件呢?例如表单一点击提交按钮(submit)跳转页面、a标签默认页面跳转或锚点定位等。

    很多时候我们使用a标签仅仅是想当做一个普通的按钮,点击实现一个功能,不想页面跳转,也不想描点定位。

    兼容性:

// prevent阻止
function preventDefault(){
    var ev = this.event
    if(ev.preventDefault){
        ev.preventDefault()
    }else{
        ev.returnValue = false
    }
}
  • event.stopPropagation() & event.stopImmediatePropagation()

    当事件句柄在捕获阶段执行时,stopPropagation阻止的是事件捕获;

    当事件句柄在冒泡阶段执行,stopPropagation阻止的是事件冒泡;

    event.stopPropagation()只会阻止冒泡或者是捕获,event.stopImmediatePropagation()除此之外还会阻止该元素的其他事件发送。

    兼容性:

// propagation传播
function stopPropagation(){
    var ev = this.event
    if(ev.stopPropagation){
        ev.stopPropagation()
    }else{
        ev.cancelBubble = true
    }
}