JavaScript - day18

39 阅读3分钟

一、面向对象继承笔试题

1.1 判断是自有还是共有

<script>
    // 判断自有还是共有
    var zs = {
        name : '张三',
        age : 18
    }
    zs.__proto__.hobby = '吃饭'
    console.log(zs)
    console.log(zs.hasOwnProperty('name'))//true
    if (zs.hasOwnProperty('hobby')) {
        console.log('自有');
    } else {
        if ('hobby' in zs) {
            console.log('共有')
        } else {
            console.log('没有')
        }
    }
</script>

1.2 删除和修改

  • 自有:
    1. 修改:obj.属性名=新属性值;
    2. 删除:delete obj.属性名;
  • 共有:
    1. 修改:原型对象.属性名=新属性值;
    2. 删除:delete 原型对象.属性名;
<script>
    var zs = {
        name : '张三',
        age : 18
    }
    zs.__proto__.hobby = '吃饭'
    console.log(zs)
    // 修改自有
    zs.age = 20
    console.log(zs)
    // 修改共有
    zs.__proto__.hobby = '睡觉'
    console.log(zs)
    // 删除自有
    delete zs.age
    console.log(zs)
    // 删除共有
    delete zs.__proto__.hobby
    console.log(zs)
</script>

1.3 为老IE的数组添加indexOf方法(如何为一类人创建某个方法)

<script>
    // 为老IE的数组创建indexOf方法
    if (Array.prototype.indexOf === undefined) {//老IE
        Array.prototype.indexOf = function (key,starti) {
            starti === undefined && (starti = 0)
            for (var i = starti; i < this.length; i++) {
                if (this[i] === key) {
                    return i
                }
            }
            return -1
        }
    }
    var arr = [1,2,34,6,8,5,2]
    console.log(arr.indexOf(2))//1
    console.log(arr.indexOf(2,2))//6
    console.log(arr.indexOf(3))//-1
</script>
<script>
    // 为老IE的字符串创建trim方法
   if (String.prototype.trim === undefined) {//老IE
        String.prototype.trim = function () {
            return this.replace(/^\s+|\s+$/g,'')
        }
   }
   var str = '   wo   '
   console.log(str.trim());//wo
</script>
<script>
    //为数组添加一个求和的方法,以及添加一个求平均值的方法
    Array.prototype.add = function () {
        for(var i = 0,sum = 0; i < this.length; i++) {
            sum += this[i]
        }
        return sum
    }
    Array.prototype.avg = function () {
        return this.add()/this.length
    }
    var arr1 = [1,2,3,4,5]
    var arr2 = [6,7,8,9,10]
    console.log(arr1.add());//15
    console.log(arr2.add());//40
    console.log(arr1.avg());//3
    console.log(arr2.avg());//8
</script>

1.4 如何判断x是不是一个数组

不能用typeof(),typeof()只能检查原始类型,不能检查引用类型,如果检查引用类型得到的结果都是一个object。

<script>
    // 判断x是不是数组
    var obj={"name":"啦啦啦"}
    var arr=[1,2,3,4]
    var now=new Date()
    var reg=/\s/
    // 1、判断x是否继承自Array.prototype
    console.log(Array.prototype.isPrototypeOf(obj))//false
    console.log(Array.prototype.isPrototypeOf(arr))//true
    console.log(Array.prototype.isPrototypeOf(now))//false
    console.log(Array.prototype.isPrototypeOf(reg))//false
    // 2、判断是不是由Array这个构造函数创建的
    console.log(obj instanceof Array)//false
    console.log(arr instanceof Array)//true
    console.log(now instanceof Array)//false
    console.log(reg instanceof Array)//false
    // 3、Array.isArray(x); - ES5新增的方法,只有数组可以这么使用
    console.log(Array.isArray(obj))//false
    console.log(Array.isArray(arr))//true
    console.log(Array.isArray(now))//false
    console.log(Array.isArray(reg))//false
    // 4、输出【对象的字符串】形式
    console.log(Object.prototype.toString.call(obj) === '[object Array]')//false
    console.log(Object.prototype.toString.call(arr) === '[object Array]')//true
    console.log(Object.prototype.toString.call(now) === '[object Array]')//false
    console.log(Object.prototype.toString.call(reg) === '[object Array]')//false
</script>

1.5 实现自定义继承:

<script>
    // 自定义继承
    var zs = {
        "phone": "威图",
        "dog": "阿拉斯加",
        "gf": "很多",
    }
    var zh = {
        "money":10000000
    }
    zs.__proto__ = zh
    console.log(zs);
    console.log(zh);
    console.log(zs.money);
</script>
<script>
    // 批量设置继承
    function h5(name,age,hobby) {
        this.name = name
        this.age = age
        this.hobby = hobby
    }
    var pg = {
        js : 200
    }
    h5.prototype = pg
    var zs = new h5('张三',18,'吃饭')
    console.log(zs)
</script>

二、class关键字

简化面向对象(封装、继承、多态)
<script>
    class lizi {
        constructor(price,weight) {
            // 里面放的都是自有属性
            this.price = price
            this.weight = weight
        }
        // 共有方法
        total() {
            return `提子的总价为:` + this.price * this.weight
        }
    }
    class fruit extends lizi {
        //extends的作用:1、继承自的自有属性全部都不用再写,只需要写一次super(),2、自动继承到共有方法
        constructor(price,weight,kind) {
            super(price,weight)
            this.kind = kind
        }
        total() {
            return `${this.kind}的总价为:` + this.price * this.weight
        }
    }
    var ti = new lizi(11,5)
    console.log(ti)
    console.log(ti.total());

    var fru = new fruit(8,4,'苹果')
    console.log(fru)
    console.log(fru.total())
</script>

三、闭包

3.1 作用域:

  • 全局:随处可用,可以反复使用,缺点:容易被污染
  • 函数:只能在函数调用时内部可用,不会被污染,缺点:一次性的,是会自动释放的

3.2 函数的执行原理:

  1. 程序加载时:
    • 创建执行环境栈(ECS):保存函数调用顺序的数组
    • 首先压入全局执行环境(全局EC)
    • 全局EC引用着全局对象window
    • window中保存着我们全局变量
  2. 定义函数时:
    • 创建函数对象:封装代码段
    • 在函数对象中有一个scope(作用域)属性:记录着函数来自的作用域是哪里
    • 全局函数的scope都是window
  3. 调用前:
    • 在执行环境栈(ECS)压入新的EC(函数的EC)
    • 创建出活动对象(AO):保存着本次函数调用时用到的全局变量
    • 在函数的EC中有一个scope chain(作用域链)属性引用着AO
    • AO有一个parent属性是函数的scope引用着的对象
  4. 调用时:
    • 正是因为有前面三步,才来带变量的使用规则:优先使用自己的,自己没有找全局,全局没有就报错
  5. 调用完:
    • 函数的EC会出栈,没人引用AO,AO自动释放,局部变量也就释放了

3.3 闭包:

希望保护一个可以【反复使用的局部变量】的一种词法结构,其实还是一个函数,只是写法比较特殊

  • 何时使用:希望保护一个可以【反复使用的局部变量】的时候
  • 如何使用:
    1. 两个函数进行嵌套
    2. 外层函数创建出受保护的变量
    3. 外层函数return出内层函数
    4. 内层函数再操作受保护的变量
  • 判断是不是闭包:有没有两个函数嵌套,返回了内层函数,内层函数再操作受保护的变量
  • 外层函数调用了几次,就创建了几个闭包,受保护的变量就有了几个副本
  • 同一次外层函数调用,返回的内层函数,都是在操作同一个受保护的变量
  • 缺点:受保护的变量,永远都不会被释放,使用过多,会导致内存泄漏
<script>
    function factory() {
        var i = 0
        return function () {
            i ++
            return i
        }
    }
    var icbc = factory()
    console.log(icbc())//1
    console.log(icbc())//2
    console.log(icbc())//3
    var abc = factory()
    console.log(abc())//1
</script>

四、防抖节流

  • elem.onmousemove - 鼠标移动事件
<script>
    div.onmousemove = function () {
        inner()
    }
    function fdjl() {
        var timer = null
        return function() {
            if (timer !== null) {
                clearTimeout(timer)
                timer = null
            }
            timer = setTimeout( ()=> {
                div.innerHTML ++
            },1000)
        }
    }
    var inner = fdjl()
</script>
  • input.oninput - 每次输入/改变都会触发
<script>
    inp.oninput = function () {
        inner()
    }
    function fdjl() {
        var timer = null
        return function () {
            if (timer !== null) {
                clearTimeout(timer)
                timer = null
            }
            timer = setTimeout( () => {
                console.log(inp.value)
            },1000)
        }
    }
    var inner = fdjl()
</script>
  • onresize - 每次窗口改变大小都会触发
<script>
    window.onresize = function () {
        inner()
    }
    function fdjl() {
        var timer = null
        return function() {
            if (timer !== null) {
                clearTimeout(timer)
                timer = null
            }
            timer = setTimeout( ()=> {
                if (innerWidth > 1280) {
                    div.style.backgroundColor = '#666'
                } else if (innerWidth > 648) {
                    div.style.backgroundColor = '#fc1'
                } else {
                    div.style.backgroundColor = 'skyblue'
                }
            },1000)
        }
    }
    var inner = fdjl()
</script>

五、两链一包

5.1 作用域链

以函数的EC的scope chain属性为起点,经过AO,逐级引用,形成的一条链式结构,我们就称之为叫做作用域链

  • 作用:查找变量,带来了变量的使用规则:优先使用自己的,自己没有找全局,全局没有就报错

5.2 原型链

每个对象都有一个属性叫做.proto,可以一层一层的找到每个对象的原型对象,最顶层的就是Object的原型,形成的一条链式结构,我们就称之为叫做原型链

  • 作用:查找属性和方法

5.3 闭包

希望保护一个可以【反复使用的局部变量】的一种词法结构

  • 作用:专门用于防抖节流