欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

js 面试题

程序员文章站 2022-06-01 16:34:36
...

javascript的数据类型
原始类型:null,undefined,number(数字类型),string(字符串类型),boolean(布尔值类型),symbol(es6新增的符号类型)
复合类型:object
javascript强制转换和隐式转换
强制类型转换是指将基本类型显示转化为包装类型(装箱)。例如:Number,String,Boolean,parseInt,parseFloat
隐式类型转换是指将基本类型隐式转化为其他类型。例如:运算符的加减乘除,等于,小于,大于,判断类型,native调用
事件绑定和普通事件的区别
普通事件不支持多个事件的绑定;对于多个绑定事件,起作用的是最后一个事件;
事件绑定支持多个事件的绑定,但是不兼容低版本IE;绑定的事件可以取消;还支持事件的冒泡和事件捕获;
普通事件的代码格式是

        element[on + 'event-name'/*小写的事件名称*/] = function () {
            ......
        }

事件绑定代码格式是

        // 绑定事件
        element.addEventlistener('event-name'/*小写的事件名称*/, function functionName () {
            ......
        }, captrue)
        // 取消事件
        element.removeEventlistener('event-name'/*小写的事件名称*/, functionName, captrue)

IE和DOM事件流的区别
IE使用冒泡类型,其他浏览器使用捕获类型
IE的事件的参数和其他类型的事件类型的参数不同
阻止事件的传递
call和apply的区别
参数列表不同,它们的第一个参数都是this的指向;但是call后面可以跟着多个参数,而apply只有两个参数,第二个参数必须为参数列表或者数组。
bind()函数的作用和call()函数是一样的。

如何阻止事件冒泡和默认事件

    // 兼容IE
    element.cancelBubble = false
    element.stopPropagation()
    return false

javascript实现继承
构建父类

    function Animate () {
        this.name = arguments[0] || 'animate'
        this.sleep = function () {
            console.log(`${this.name} start sleep.`)
        }
    }
    Animate.prototype.eat = function () {
        console.log(`${this.name} start eat.`)
    }

原型链继承

        function Cat () {}
        Cat.prototype = new Animate()

特点:

非常纯粹的继承关系,实例是子类的实例,也是父类的实例

父类新增原型方法/原型属性,子类都能访问到

简单,易于实现

缺点:

要想为子类新增属性和方法,必须要在new Animal()这样的语句之后执行,不能放到构造器中

无法实现多继承

来自原型对象的所有属性被所有实例共享

创建子类实例时,无法向父类构造函数传参

构造继承

        function Cat () {
            Animate.call(this, arguments)
        }

特点:

解决了1中,子类实例共享父类引用属性的问题

创建子类实例时,可以向父类传递参数

可以实现多继承(call多个父类对象)

缺点:

实例并不是父类的实例,只是子类的实例

只能继承父类的实例属性和方法,不能继承原型属性/方法

无法实现函数复用,每个子类都有父类实例函数的副本,影响性能

实例继承

        function Cat () {
            var instance = new Animate()
            instance.name = arguments[0] || 'animate'
            return instance
        }

特点:

不限制调用方式,不管是new 子类()还是子类(),返回的对象具有相同的效果

缺点:

实例是父类的实例,不是子类的实例

不支持多继承

拷贝继承

        function Cat () {
            var animate = new Animate()
            for (var p in animate) {
                Cat.prototype[p] = animate[p]
            }
            Cat.prototype.name = arguments[0] || 'animate'
        }

特点:

支持多继承

缺点:

效率较低,内存占用高(因为要拷贝父类的属性)

无法获取父类不可枚举的方法(不可枚举方法,不能使用for in 访问到)

组合继承

        function Cat () {
            Animate.call(this, arguments)
            this.name = arguments[0] || 'animate'
        }

        Cat.prototype = new Animate()
        Cat.constructor = Cat

特点:

弥补了方式2的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法

既是子类的实例,也是父类的实例

不存在引用属性共享问题

可传参

函数可复用

缺点:

调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)

寄生组合继承

        function Cat () {
            Animate.call(this, arguments)
            this.name = arguments[0] || 'animate'
        }
        (function () {
            function Super () {}
            Super.prototype = Animate.prototype
            Cat.prototype = new Super()
            Cat.prototype.constructor = Cat
        }())

特点:

核心:通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点

缺点:

实现较为复杂

数组去重
使用es6的Set

        function deRepeat () {
            const arr = arguments[0]
            if (Array.isArray(arr)) {
                return Array.from(new Set(arr))
            } else {
                return []
            }
        }    

使用对象的属性

        function deRepeat () {
            const arr = arguments[0]
            if (!Array.isArray(arr)) {
                return []
            }
            let obj = {}
            let result = []
            
            arr.forEach(function (value) {
                if (!obj.hasOwnProperty(value)) {
                    obj[value] = 1
                }
            })
            if (Object.keys) {
                result = [...Object.keys(obj)]
            } else {
                for (var prop in obj) {
                    if (obj.hasOwnProperty(prop)) {
                        result.push(prop)
                    }
                }
            }
            return result.map(function (value) {
                return parseInt(value)
            })
        }

forEach加上indexOf

        function deRepeat () {
            const arr = arguments[0]
            if (!Array.isArray(arr)) {
                return []
            }
            const result = []
            let currIndex
            arr.forEach(function (value, index) {
                currIndex = arr.indexOf(value, index + 1)
                if (currIndex == -1) {
                    result.push(value)
                }
            })
            return result
        }    

添加 删除 替换 插入到某个节点的方法
parentNode.appendChild(node)
parentNode.removeChild(node)
parentNode.replaceChild(oldNode, newNode)
parentNode.insertBefore(node)
同源策略
同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。

一段脚本只能读取来自于同一来源的窗口和文档的属性,这里的同一来源指的是主机名、议和端口号的组合

用js实现随机选取10–100之间的10个数字,存入一个数组,并排序。

    function randomSort (max = 100, min = 10, len = 10) {
        function checkParam (num) {
            if (Number.isInteger(num) && num > 0) {
                return true
            } else {
                return false
            }
        }
        if (!checkParam(max) || !checkParam(min) || !checkParam(len) || max < min) {
            return []
        }
        const result = []
        let randomNumber
        while (len > 0) {
            randomNumber = Math.ceil(Math.random() * (max - min) + min)
            result.push(randomNumber)
            --len
        }
        return result.sort((a, b) => {
            return a > b
        })
    }

有这样一个URL:http://item.taobao.com/item.htm?a=1&b=2&c=&d=xxx&e,请写一段JS程序提取URL中的各个GET参数(参数名和参数个数不确定),将其按key-value形式返回到一个json结构中,如{a:’1′, b:’2′, c:”, d:’xxx’, e:undefined}。

    function serilizeUrl (url) {
        // 排除非字符串或空字符串
        function checkString (str) {
            if (typeof str != 'string' || str == '') {
                return false
            } else {
                return true
            }
        }
        const result = {}
        // 如果url是字符串,且字符串中含有?,则获取?后面的字符串,且该字符串不为空字符串
        if (checkString(url) && url.indexOf('?') > -1 && checkString(url.split('?')[1])) {
            url.split('?')[1].split('&').map(paramString => {
                // 依据&分解参数,将参数分解成key-value组成的数组,如果value为空,则默认为undefined
                if (paramString.indexOf('=') > -1) {
                    return paramString.split('=')
                } else {
                    return [paramString, undefined]
                }
            }).forEach(paramsArr => {
                // 将分解的结果(一个二维数组),添加到对象上
                Object.defineProperty(result, String(paramsArr[0]), {
                    value: paramsArr[1],
                    writable: true,
                    enumerable: true,
                    configurable: true
                })
            })
        }
        return result
    }

写一个function,清除字符串前后的空格。(兼容所有浏览器)

    function setCommonTrim () {
        if (!String.prototype.trim) {
            Object.defineProperty(String.prototype, 'trim', {
                value: function () {
                    return this.replace(/^\s+/, '').replace(/\s+$/, '')
                },
                writable: false,
                enumerable: false,
                configurable: false
            })
        }
    }
    setCommonTrim()

Javascript中callee和caller的作用?
caller是返回一个对函数的引用,该函数调用了当前函数;
callee是返回正在被执行的function函数,也就是所指定的function对象的正文。
那么问题来了?如果一对兔子每月生一对兔子;一对新生兔,从第二个月起就开始生兔子;假定每对兔子都是一雌一雄,试问一对兔子,第n个月能繁殖成多少对兔子?(使用callee完成)
① 每月小兔对数=上月大兔对数.

② 每月大兔对数等于上个月大兔对数,与小兔对数之和.

综合①②两点,我们就有:每月大兔对数等于前两个月大兔对数之和.

公式是:每月大兔对数 = 上个月大兔对数 + 上个月小兔对数 = 上个月大兔对数 + 上上个月大兔对数。符合费波那西数列。

        function fabonacci (n = 1) {
            if (!Number.isInteger(n) || n < 1) {
                throw new TypeError('function fabonacci () TypeError: Param must be a integer number!')
            }
            if (n < 3) {
                return 1
            } else {
                // return arguments.callee(n - 1) + arguments.callee(n - 2)
                return fabonacci(n - 1) + fabonacci(n - 2)
            }
        }

        function getSum (n = 1) {
            let result = 0
            while (n > 0) {
                result += fabonacci(n)
                --n
            }
            return result
        }

        console.log(getSum(12))
        console.log(getSum(24))

js延迟加载的方式有哪些?
使用defer属性或async属性。defer属性:用途:表明脚本在执行时不会影响页面的构造。也就是说,脚本会被延迟到整个页面都解析完毕之后再执行。async属性:不让页面等待脚本下载和执行,从而异步加载页面其他内容。异步脚本一定会在页面 load 事件前执行。不能保证脚本会按顺序执行。
动态脚本
按需异步加载js文件