JS题目

作者: 头大如牛 | 来源:发表于2019-07-11 00:09 被阅读0次

    JS

    1、原型/原型链/构造函数/实例/继承

    1. proto(原型)

    每个对象又有proto属性,指向创建他的构造函数的原型对象(实例指向原型对象的指针)

    2.prototype原型对象

    每个函数都有一个prototype属性,是指向一个对象的引用,这个对象成为原型对象,原型对象包含函数实例共享的方法和属性,也就是说将函数用作new时,新创建的对象会从原型上继承属性和方法

    3.原型链

    原型可以通过proto访问到原型的原型,比如构造函数Person继承前者的有一个构造函数People,然后new People得到实例p

    image
    image

    4.构造函数Constructor 实例、

    new运算符创建的函数,其实就是构造函数,构造函数创建出的对象,就是实例

    5.继承

    子类可以使用父类的所有功能,并且对这些功能进行扩展。继承的过程,就是从一般到特殊的过程

    2、有几种方式可以实现继承

    6种方式实现继承

    想要继承,必须有一个父类

    function Person(name){
        this.name=name
        this.sum = function(){
            alert(this.name)
        }
    }
    Person.prototype.age = 10;
    
    1. 原型链继承
    function Per(){
        this.name = "ker"
    }
    Per.prototype = new Person();//原型链继承
    var per1 = new Per();
    console.log(per1.age)//10
    // instanceof 判断元素是否早另一个元素的原型链上
    // per1 继承了Person的属性,返回true
    console.log(per1 instanceof Person)
    

    重点:需要让新实例的原型,被父类的实例赋值
    优点:实例可继承的属性有: 实例的构造函数的属性,父类构造函数属性,父类原型的属性
    缺点:1. 新实例无法向父类构造函数传参
    2. 继承单一
    3. 所有新实例都会共享父类实例属性,原型上的属性是共享的,一个实例修改了原型属性,所有实例的原型属性也会被修改

    1. 构造函数继承
    function Con(){
        Person.call(this,"jer")//重点
        this.age = 12;
    }
    var con1 = new Con();
    console.log(con1.name);//"jer"
    console.log(con1.age);//12
    console.log(con1 instanceof Person)//false
    

    重点:用call()和apply()将父类构造函数引入子类函数
    优点: 1. 只继承了父类构造函数的属性,没有继承父类原型属性
    2. 解决了原型链继承缺点123
    3. 可以继承多个构造函数属性(call多个)
    4. 在子实例中可以向父实例传参
    缺点: 1. 只能继承附列构造函数属性
    2. 无法实现构造函数的复用(每次用每次都要重新调用)
    3. 每个新实例都有父类构造函数的副本,臃肿

    1. 组合继承(组合原型链继承和构造函数继承)(常用)
    function SubType(name){
        Person.call(this,name)//借用构造函数继承
    }
    SubType.prototype = new Person()//原型链继承
    var sub = new SubType("gar")
    console.log(sub.name)//“gar”继承了构造函数属性
    console.log(sub.age);//10 继承了父类原型的属性
    

    重点: 结合了两种模式的有点,传参和复用
    优点: 1. 可以继承父类原型上的属性,可以传参,可以复用
    2. 每个新实例引入的构造函数是私有的
    缺点: 调用两次父类的构造函数,子类的构造函数会代替原型上那个父类构造函数

    1. 原型式继承
    // 先封装一个函数容器,用来输出对象和承载继承的原型
    function content(obj){
        function F(){}
        F.prototype = obj;//继承了传入的参数
        return F(); //返回函数对象
    }
    var sup = new Person();// 拿到父类实例
    var sup1 = content(sup);
    console.log(sup1.age);// 10 继承了父类函数的属性
    

    重点: 用函数包裹对象,然后返回函数的调用,这个函数就变成了可以随意添加属性的实例或对象,object.create()就是这个原理
    特点: 类似于复制一个对象,用函数来包装
    缺点: 1. 所有实例都会继承原型上属性
    2. 无法实现复用,新实例属性都是后面添加的

    1. 寄生式继承
    function content(obj){
        function F(){}
        F.prototype = obj
        return new F();
    }
    var sup = new Person();
    function subobject(obj){
        var sub = content(obj)
        sub.name = "gar"
        return sub;
    }
    var sup2 = subobject(sup)
    console.log(typeof subobject)//function
    console.log(typeof sup2)//object
    console.log(sup2.name)//"gar" 返回了个sub对象,继承了sub的属性
    

    重点:就是给原型式继承外面套了壳子
    特点: 没有创建自定义类型,因为只是套了壳子返回对象,这个函数也就成为了新对象
    缺点:没用到原型,无法复用

    1. 寄生组合式继承(常用)

    3、用原型实现继承有什么缺点,怎么解决

    缺点

    1. 重写子类的原型 等于 父类的一个实例,(父类的实例属相变成子类的原型属性)如果父类包含引用类型的属性,那么子类所有实例都会共享该属性

    2. 在创建子类实例时,不能向父类的构造函数传递参数

    解决办法:组合继承解决原型链继承的引用类型原型属性被实例共享问题

    4、arguments

    它是js的一个内置对象,常被忽略,js不像JAVA是显示传递参数,js传的是形参,可以传也可以不传,若方法里面没有写参数,却传入了参数,可用arguments拿到参数
    每一个函数都有一个arguments对象,他包括了函数所要调的参数,通常我们把它当作数组使用,用它的length得到参数数量,但它是类数组对象,无法使用push

    5、数据类型判断

    1. typeof
    typeof '';//string 有效
    typeof 1; // number 有效
    typeof Symbol();//symbol 有效
    typeof true; // boolean
    typeof undefined; //undefined 有效
    typeof null; // object 无效
    typeof []; // object 无效
    typeof new Function(); // functionyoux 
    typeof new Date(); // object 无效
    typeof new RegExp; //object 无效
    
    1. instanceof
      用于判断A是否是B的实例,表达式为 A instanceof B

    2. constructor

    console.log('数据类型判断' - constructor);
    console.log(arr.constructor === Array);//true
    console.log(date.constructor === Date); // true
    console.log(fn.constructor === Function); // true
    
    1. toString
    Object.prototype.toString.call('');//[object String]
    Object.prototype.toString.call(1); //[object Nmuber]
    Object.prototype.toString.call(document)//[object HTMLDocument]
    Object.prototype.toString.call(window)//[object global]
    

    6、作用域链、闭包、作用域

    作用域

    js的作用域是靠函数形成的,函数外不可访问函数内的变量,作用域就是变量和函数的可访问的范围。

    作用域链

    当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)来保证对执行环境有权访问的变量和函数的有序访问

    闭包

    能够访问另一个函数作用域中变量的函数

    function outer() {
        var a = '变量1'
        var inner = function () {
            console.info(a)
        }
        return inner //inner就是一个闭包函数,因为他可以访问outer函数的作用域
    }
    

    闭包的问题

    1. 引用的变量可能发生变化
    2. this指向
    3. 内存泄漏

    闭包的作用

    1. 解决递归调用
    2. 模仿块级作用域

    7、Ajax的原生写法

    function ajax(opts){
        var xhr = new XMLHttpRequest();//创建ajax对象
        xhr.onreadystatechange = fucntion(){
            if(xhr.readyState ===4 & xhr.status===200){
                opts.success(JSON.parse(xhr.responseText))
            }else if(xhr.readyState === 4 & xhr.status !== 200){
                opts.error();
            }
        }
        var urlStr = ''
        for (var key in opts.data){
            urlStr = key + '=' + opts.data[key] + '&';
        }
        urlStr = urlStr.substing(0, urlStr.length-1);
        if(opts.type.toLowerCase() === 'get'){
            xhr.open(opts.type, opts.url + '?' + urlStr, true)
            xhr.send()
        }
        if(opts.type.toLowerCase() === 'post'){
            xhr.open('post', opts.url, true);
            xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
            xhr.send(urlStr);
        }
    };
    btn.addEventListener('click', function(){
        ajax({
            url: 'list.json',
            type: 'post',
            success: fucntion(data){
                console.log(data.list)
            },
            error: function(){
                console.log('发生了错误...')
            }
        })
    })
    

    8、对象深拷贝、浅拷贝

    对象是引用类型,所以直接赋值修改赋值后的数值,原对象会改变

    var boy = {
        age: 18
    }
    var girl = boy;
    console.log(boy === girl) //true
    girl.age = 20
    console.log(boy.age);//20
    

    浅拷贝

    var boy = {
        age: 18,
        address: {
            home: '天堂'
        }
    }
    var girl = Object.assign({},boy);
    console.log(boy === girl) //false
    girl.age = 20;
    console.log(boy.age);//18
    girl.address.home = '上海'
    console.log(boy.address.home);//上海
    

    因为Object.assign()只是浅拷贝girl.address是对栈对象的引用,因此内层对象的修改会影响原始对象。

    深拷贝

    1. JSON.parse()和JSON.stringify()深拷贝(仅适用纯JSON对象)
    function deepClone(obj) {
        return JSON.parse(JSON.stringify(obj));
    }
    
    1. 对象遍历
    
    //包含其他负责的内容 date对象 null undefined
    var obj1={
        name:"张三",
        age:20,
        height:[12,26,46],
        address:{
            home:'北京'
        },
        birthday:new Date(),
        father:null,
        mother:undefined,
        school:[
                {
                    middleschool:'北大附中',
                },
                {
                    university:'清华大学',
                }
            ]
        }
        function clone(obj) { 
            if(obj === null) return null 
            if(typeof obj !== 'object') return obj;
            if(obj.constructor === Date) return new Date(obj); 
            if(obj.constructor === RegExp) return new RegExp(obj);
            var newObj = new obj.constructor ();  //保持继承链
            for (var key in obj) {
             if (obj.hasOwnProperty(key)) {   //不遍历其原型链上的属性
                  var val = obj[key];
                  newObj[key] = typeof val === 'object' ? arguments.callee(val) : val; // 使用arguments.callee解除与函数名的耦合
              }
                }  
                return newObj;  
        }; 
            console.log(obj1);
        console.log(clone(obj1));
    
    
    1. 用 new obj.constructor ()构造函数新建一个空的对象,可以保持原形链的继承;

    2. 用obj.hasOwnProperty(key)来判断属性是否来自原型链上,因为for..in..也会遍历其原型链上的可枚举属性。

    3. 函数用到递归算法,在函数有名字,而且名字以后也不会变的情况下,这样定义没有问题。但问题是这个函数的执行与函数名 factorial 紧紧耦合在了一起。为了消除这种紧密耦合的现象,需要使用 arguments.callee。

    9、图片懒加载、预加载

    图片预加载

    即提前加载图片,可保证图片快速、无缝地发布,用户需要查看时可直接从本地缓存中渲染,适用于图片占据很大比例的网站。

    1. js new image对象 设置src加载
    function preloader(){
        if (document.images) {
            var img1 = new Image()
            var img2 = new Image()
            var img3 = new Image()
            img1.src = "";
            img2.src = "";
            img3.src = "";
        }
    }
    
    function addLoadEvent(func) {
        var oldonload = window.onload;
        if (typeof window.onload != 'fucntion') {
            window.onload = func;
        }else {
            window.onload = function (){
                if (oldonload) {
                    oldonload();
                }
                func();
            }
        }
    }
    addLoadEvent(preloader);
    div.appendChild(img1)//插入到DOM
    
    1. Ajax预加载, new Image()对象设置src
    window.onload = function() {
        setTimeout(function() {
            var xhr = new XMLHttpRequest();
            xhr.open('GET',url);
            xhr.send();
            xhr = new XMLHttpRequest();
            xhr.open('GET', url)
            xhr.send()
            // preload image
            new Image().src = '';
        }, 1000);
    }
    
    

    懒加载

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Lazyload 2</title>
        <style>
        img {
            display: block;
            margin-bottom: 50px;
            height: 200px;
        }
        </style>
    </head>
    <body>
        <img src="images/loading.gif" data-src="images/1.png">
        <img src="images/loading.gif" data-src="images/2.png">
        <img src="images/loading.gif" data-src="images/3.png">
        <img src="images/loading.gif" data-src="images/4.png">
        <img src="images/loading.gif" data-src="images/5.png">
        <img src="images/loading.gif" data-src="images/6.png">
        <img src="images/loading.gif" data-src="images/7.png">
        <img src="images/loading.gif" data-src="images/8.png">
        <img src="images/loading.gif" data-src="images/9.png">
        <img src="images/loading.gif" data-src="images/10.png">
        <img src="images/loading.gif" data-src="images/11.png">
        <img src="images/loading.gif" data-src="images/12.png">
        <script>
    
        function throttle(fn, delay, atleast) {//函数绑定在 scroll 事件上,当页面滚动时,避免函数被高频触发,
            var timeout = null,//进行去抖处理
            startTime = new Date();
            return function() {
            var curTime = new Date();
            clearTimeout(timeout);
            if(curTime - startTime >= atleast) {
                fn();
                startTime = curTime;
            }else {
                timeout = setTimeout(fn, delay);
            }
            }
        }
        function lazyload() {
            var images = document.getElementsByTagName('img');
            var len    = images.length;
            var n      = 0;      //存储图片加载到的位置,避免每次都从第一张图片开始遍历        
            return function() {
            var seeHeight = document.documentElement.clientHeight;
            var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
            for(var i = n; i < len; i++) {
                if(images[i].offsetTop < seeHeight + scrollTop) {
                    if(images[i].getAttribute('src') === 'images/loading.gif') {
                     images[i].src = images[i].getAttribute('data-src');
                    }
                n = n + 1;
                 }
            }
            }
        }
        var loadImages = lazyload();
        loadImages();          //初始化首页的页面图片
        window.addEventListener('scroll', throttle(loadImages, 500, 1000), false);
      //函数节流(throttle)与函数去抖(debounce)处理,
    //500ms 的延迟,和 1000ms 的间隔,当超过 1000ms 未触发该函数,则立即执行该函数,不然则延迟 500ms 执行该函数
        </script>
    </body>
    </html>
    

    10、实现页面加载进度条

    11、this关键字

    12、函数式编程

    13、手动实现parseInt

    function _parseInt (string, radix) {
        if (typeof string !== "string" && typeof string !== "number") return NaN;
        if (radix && (typeof radix !== "number" || /^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$/.test(radix) || radix > 36 || radix < 2)) return NaN;
        string = String(string)
        var rexp = (radix == 10) ? /(-?)([0]?)([0-9]+)/ : /(-?)([0]?[Xx]?)([0-9a-fA-F]+)/,
            a = string.match(rexp),
            sign = a[1],
            rawRadix = a[2],
            rawNum = a[3],
            result = 0,
            strArr = rawNum.split(''),
            len = strArr.length,
            numArr = [];
        if (a && !radix) {
            if ( rawRadix.toUpperCase() === "0X") {
                radix = 16;
            } else if (rawRadix === "0") {
                radix = 8;
            } else {
                radix = 10;
            }
        }
        for (var i = 0; i < len; i++){
            var num;
            var charCode = strArr[i].toUpperCase().charCodeAt(0);
            if(radix <=36 && radix >= 11) {
                if (charCode >= 65 && charCode <= 90) {
                    num = charCode - 55;
                } else {
                    num = charCode - 48;
                }
            }  else {
                num = charCode - 48;
            }
            if (num < radix) {
                numArr.push(num);
            } else {
                return NaN
            };
        }
        if(numArr.length > 0) {
          numArr.forEach(function(item, j){
              result += item * Math.pow(radix, numArr.length-j-1);
          })
        }
        if(sign === "-"){
          result = -result;
        }
        return result
    }
    
    // 以下例子均返回15:
    console.log(_parseInt("F", 16));
    console.log(_parseInt("17", 8));
    console.log(_parseInt("15", 10));
    console.log(_parseInt(15.99, 10));
    console.log(_parseInt("FXX123", 16));
    console.log(_parseInt("1111", 2));
    console.log(_parseInt("15*3", 10));
    console.log(_parseInt("12", 13));
    
    // 以下例子均返回 NaN:
    console.log(_parseInt("Hello", 8)); // Not a number at all
    console.log(_parseInt("546", 2));   // Digits are not valid for binary representations
    
    // 以下例子均返回 -15:
    console.log(_parseInt("-F", 16));
    console.log(_parseInt("-0F", 16));
    console.log(_parseInt("-0XF", 16));
    console.log(_parseInt(-15.1, 10));
    console.log(_parseInt(" -17", 8));
    console.log(_parseInt(" -15", 10));
    console.log(_parseInt("-1111", 2));
    console.log(_parseInt("-15e1", 10));
    console.log(_parseInt("-12", 13));
    // 下例中也全部返回 17,因为输入的 string 参数以 "0x" 开头时作为十六进制数字解释,而第二个参数假如经过 Number 函数转换后为 0 或 NaN,则将会忽略。
    console.log(_parseInt("0x11", 16));
    console.log(_parseInt("0x11", 0));
    console.log(_parseInt("0x11"));
    
    // 下面的例子返回 224
    console.log(_parseInt("0e0",16));
    
    

    14、为什么会有同源策略

    • 协议、域名、端口号一致,表示同源
    • 不能通过ajax请求不同源的数据,不能通过脚本操作不同域下的DOM
    • 设置同源限制主要是为了安全,如果没有同源限制存在浏览器中的cookie等其他数据可以任意读取,不同域下DOM任意操作,ajax任意请求的话如果浏览了恶意网站那么就会泄漏这些隐私数据

    15、怎么判断两个对象是否相等

    // 传入两个对象
    function isObjectValueEqual(a, b) {
        // Object.getOwnPropertyNames()
        var aProps = Object.getOwnPropertyNames(a);
        var bProps = Object.getOwnPropertyNames(b);
        if (aProps.length != bProps.length) {
            return false;
        }
        for (var i = 0; i < aProps.length; i++) {
            var propName = aProps[i];
    
            if (a[propName] !== b[propName]) {
                return false;
            }
        }
        return true;
    }
    

    16、事件模型

    事件与事件流

    事件是浏览器与文档交互的瞬间,比如点击按钮,填写表格等操作,它是Javascript与HTML之间沟通的桥梁,DOM是树状结构,如果同时给父节点绑定时间,触发子节点时,两个事件发生的顺序就牵涉到事件流的内容,它描述的是页面接受事件的顺序,事件流描述的是从页面接受事件的顺序,但是IE的事件流食冒泡流,而Netspace Communicator的事件流是捕获流

    • IE的事件流叫做事件冒泡。即事件开始由最具体的元素接收,然后逐级向上传播到不具体的节点。事件捕获则相反,是不太具体的节点硬更早接收事件,具体的节点应该最后接收事件。
    1. DOM0级事件模型

    是早期的事件模型,又称为原始事件模型,在该模型中,事件不会传播,即没有事件流的概念。事件绑定监听函数较为简单,要使用js指定事件处理程序,首先取得一个要操作的对象的引用。
    每个元素都有自己的事件处理属性,通常全部小写,例如onclick,设置为函数即可指定事件处理程序:

    btn = document.getElementById("myBtn" = "Clicked!")
    
    // HTML事件处理程序
    <form method = "post">
        <input type="text" name="username" value="">
        <input type="button" value="Username" onclick="alert(username.value)">
    </form>
    
    1. DOM2级事件模型

    在该模型中,分为三个过程:事件捕获,处于目标阶段,事件冒泡阶段

    • 事件捕获阶段。事件从document一直向下传播到目标元素,依次检查经过的节点是否绑定了时间监听函数,如果有则执行
    • 事件处理阶段。事件达到目标元素,触发目标元素的监听函数
    • 事件冒泡阶段。事件从目标元素冒泡到document,一次检查经过节点是否绑定了事件监听函数,如果有则执行,定义了两个方法addEventListener()和removeEventListener()。所有DOM节点都包含这两个方法,并且有三个参数,要处理的事件名、作为事件处理程序的函数和一个布尔值。要在click事件添加事件处理程序,可以用:
    var btn = document.getElementById("myBtn");
    btn.addEventListener("click",function(){
        alert(this.id);
    }, false);
    btn.addEventListener("click",function(){
        alert("Hello kid")
    },false)
    

    执行顺序为"myBtn","Hello kid"。IE中执行顺序相反

    移除事件监听方式如下

    btn.removeEventListener("click")
    
    1. IE中的事件模型
    var btn = document.getElementById("myBtn");var handler = function() {
    
        alert(this.id);
    
    };
    
    btn.attachEvent("onclick", handler);//添加事件处理程序btn.detachEvent("onclick", handler);//删除事件处理程序
    

    事件委托、代理

    利用事件冒泡的原理,把目标事件委托给父元素

    优点:

    1. 管理函数变少了,不需要为每个元素都添加监听函数,对于同一个父节点下面类似的子元素,可以通过委托给父元素的监听函数来处理
    2. 可以方便地动态添加和修改元素,不需要因为元素的改动而修改事件绑定。(比如后来添加子元素依然拥有事件)
    3. JavaScript和DOM节点之间的关联变少了,这样也就减少了因循环引用而带来的内存泄漏发生的概率。

    应用场景

    很多商品放在一个ul下面的li标签里面,点击添加或删除商品,就可以绑定商品的父元素ul标签,通过事件代理去找到要点击的商品,完成添加删除事件

    如何让事件先冒泡后捕获

    17、window的onload事件和domcontentloaded

    1. 当onload触发时,页面上所有的DOM,CSS,script,图片,flash等全部加载完成了
    2. DOMContentLoaded事件触发时,仅仅DOM加载完成,其余均未完成

    18、for...in迭代和for...of有什么区别

    for in

    // for in 应用于数组
    
    Array.prototype.sayHello = function(){
        console.log("Hello")
    }
    Array.prototype.str = "world";
    var myArray = [1,2,10,30,100];
    myArray.name = "数组"
    
    for(let index in myArray) {
        console.log(index);
    }
    // 结果: "0" "1" "2" "3" "4" "name" "sayHello" "str"
    
    // for in 应用于对象中
    Object.prototype.sayHello = function(){
        console.log('Hello');
    }
    Object.prototype.str = "World";
    var myObject = {name: 'hanlei', age:100};
    
    for(let index in myObject){
        console.log(index);
    }
    
    // 输出结果:"name" "age" "sayHello" "str"
    // 首先输出的是对象的属性名,再是对象原型的属性和方法
    //如果不想让其输出原型中的属性和方法,可以使用hasOwnProperty方法进行过滤
    
    for (let index in myObject){
        if(myObject.hasOwnProperty(index)){
            console.log(index)
        }
    }
    // 输出结果为: "name" "age"
    // 也可以通过Object.keys()方法获取所有的自身可枚举属性组成的数组
    Object.keys(myObject)
    

    经观察,for in应用于数组返回是数组下标和属性和原型上的方法和属性,而对象中返回的是对象的属性名和原型中的方法和属性
    for in遍历数组时,会存在几个问题

    1. index索引为字符串,不能进行运算
    2. 遍历顺序有可能不是按照数组内部顺序
    3. for in会遍历所有可枚举属性,包括原型

    for of

    Object.prototype.sayHello = function(){
        console.log('Hello');
    }
    var myObject =  {
        name: 'hanlei',
        age: 100
    }
    
    for(let key of myObject){
        console.log(key)
    }
    // 结果:TypeError
    
    Array.prototype.sayHello = function(){
        console.log('hello');
    }
    var myArray = [1,2 ,1231,1,56877]
    for (let key of myArray) {
        console.log(key);
    }
    

    for of遍历的是数组的元素值

    总结:

    1. 遍历对象用for in,遍历数组使用for of
    2. for in循环出的是key,for of循环出的是value
    3. 注意 for of是ES6新特性,为了修复ES5for in的不足
    4. for of 不能遍历普通对象,需要通过Object。keys()搭配使用

    19、函数柯里化

    柯里化(currying),是把接受多个参数的函数变换成接受一个单一参数的函数,并返回接受剩下参数且返回结果的新函数的技术。
    即只传给函数一部分参数调用它,让它返回一个函数,去处理剩下的参数

    function add(x, y, z) {
        return x + y + z;
    }
    console.log(add(1, 2, 3))//6
    
    var add = function(x) {
        return function(y) {
            return funciton (z) {
                return x + y + z;
            }
        }
    }
    var addOne = add(1);
    var addOneAndTwo = addOne(2);
    var addOnetoThree = addOneAndTwo(3);
    console.log(addOnetoThree)
    

    通过闭包记住第一个参数以此类推,可以用ES6的箭头函数写成这样:

    const add = x => y => z => x + y + z
    

    20、call apply区别,原生实现bind

    bind,apply,bind方法,其实是函数的另外一种调用方法,功能上等同于<code>fn</code>,只是调用者由bind,apply,call来决定,也就是<code>bind,apply,call</code>指定函数执行上下文

    声明一个函数后,基本调用方式为:

    // 方法定义
    
    var obj = {
        todo: function(){
            console.log("rushB");
        }
    }
    // 调用
    obj.todo();// "rushB"
    
    // 函数声明
    function todo() {
        cosole.log("rushA");
    }
    // 调用
    todo();
    
    // 函数表达式
    var todo = function() {
        console.log("rush不动了")
    }
    // 调用方法
    todo()
    

    接下里用<code>bind,apply,call</code>对上面进行改写

    var obj = {
        todo: function() {
            console.log("rushA");
        }
    }
    // 调用方式
    obj.todo.apply();
    obj.todo.call();
    obj.todo.bind()();
    
    // 函数声明
    function todo() {
        console.log("rushB");
    }
    
    // 调用方式
    var todo = function() {
        console.log("rush不动了")
    }
    
    // 函数表达式
    todo.apply();
    todo.call();
    todo.bind()();
    

    可以传参数,第一个参数就是受益者,this的指向对象,第二个参数是arguments

    fn.apply(otherObj);
    fn.call(otherObj);
    fn.bind(otherObj)()
    

    需要调用this才生效

    var csgo = "rushAB";
    var obj = {
        csgo: "rushA",
        fn: function(){
            console.log(this.csgo)
        }
    }
    
    obj.fn()
    
    //this指向window,获得想要的window上的属性
    obj.fn.apply(window)
    obj.fn.call(window)
    obj.fn.bind(window)()
    
    如果将this换成obj
    var obj = {
        csgo: "rushA",
        fn: function(){
            console.log(obj.csgo)
        }
    }
    obj.fn()
    
    //this指向window,没有获得window上的属性
    obj.fn.apply(window)
    obj.fn.call(window)
    obj.fn.bind(window)()
    

    三个方法允许我们明确指定方法中的this指向

    bind,aplly,call的区别

    • apply 和 call 的用法几乎相同, 唯一的差别在于当函数需要传递多个变量时, apply 可以接受一个数组作为参数输入, call 则是接受一系列的单独变量。

    • 在上面的代码片段中,我们可以看出bind使用方法和apply, call 不一样,多了一个执行()。那是因为bind在函数调用后,实际上会返回一个新函数。

    • bind,aplly,call中只有apply接收一个数组,可以通过apply和array两个单词尾部都是y记忆。bind,call则都是接收一系列单独变量。

    注意

    Array.prototype.slice.apply.(obj),可以让一个类数组对象使用数组的方法,类数组主要是指通过DOM 操作获取的DomList 和arguments,都是具有length属性的对象。

    Object.prototype.toString.call(obj).slice(8, -1) 方法获取对象属性,比typeof()更可信。
    在 ES6 的箭头函数下, call 和 apply 的失效
    不可以当作构造函数, 也就是说不可以使用 new 命令, 否则会抛出一个错误

    call,apply,bind 三者用法和区别:角度可为参数、绑定规则(显示绑定和强绑定),运行效率、运行情况。

    21、async/await

    22、立即执行函数和使用场景

    23、设计模式(要求说出如何实现,应用,优缺点)/单例模式实现

    24、iframe的缺点有哪些

    25、数组问题

    • 数组去重
    1. 遍历数组法

    实现思路:新建一个数组,遍历去要重的数组,当值不在新数组的时候(indexOf为-1)就加入该新数组中

    var arr=[2,8,5,0,5,2,6,7,2];
    function unique1(arr){
      var hash=[];
      for (var i = 0; i < arr.length; i++) {
         if(hash.indexOf(arr[i])==-1){
          hash.push(arr[i]);
         }
      }
      return hash;
    }
    
    1. 数组下标判断法

    实现思路:如果当前数组的第 i 项在当前数组中第一次出现的位置不是 i,那么表示第 i 项是重复的,忽略掉。否则存入结果数组。

    function unique2(arr){
      var hash=[];
      for (var i = 0; i < arr.length; i++) {
         if(arr.indexOf(arr[i])==i){
          hash.push(arr[i]);
         }
      }
      return hash;
    }
    
    1. 排序后相邻去除

    实现思路:给传入的数组排序,排序后相同的值会相邻,然后遍历排序后数组时,新数组只加入不与前一值重复的值

    function unique3(arr){
      arr.sort();
      var hash=[arr[0]];
      for (var i = 1; i < arr.length; i++) {
         if(arr[i]!=hash[hash.length-1]){
          hash.push(arr[i]);
         }
      }
      return hash;
    }
    
    1. 优化遍历数组法

    实现思路:双层循环,外循环表示从0到arr.length,内循环表示从i+1到arr.length

    将没重复的右边值放入新数组。(检测到有重复值时终止当前循环同时进入外层循环的下一轮判断)

    function unique4(arr){
      var hash=[];
      for (var i = 0; i < arr.length; i++) {
        for (var j = i+1; j < arr.length; j++) {
          if(arr[i]===arr[j]){
            ++i;
          }
        }
          hash.push(arr[i]);
      }
      return hash;
    }
    
    1. set
    let arr = [4, 1, 3, 3, 2, '2'];
    let uniqueArr = [...new Set(arr)];
    console.log(uniqueArr); // [4, 1, 3, 2, "2"]
    

    数组常用方法

    split()//字符串 ->数组转换
    join()//数组 ->字符串转换, 转换时可以添加符号, 还有一个是toString()
    indexOf()
      //查找元素返回第一个与参数相同的元素的索引。有另外函数 lastIndexOf(),
      //该函数返回相同元素中最后一个元素的索引,如果没找到相同元素,则返回 -1
     
    push()
    pop()
    unshift()
    shift()
     
    concat()
    splice()
    reverse()
    sort()
     
    forEach()
    every()
    some()
    reduce(function (runningTotal, currentValue) {
      return runningTotal +   currentValue;
    })
    //reduceRight()
     
    map()
    filter()
    
    • 查找数组重复项
    1. 利用sort方法,先使用sort方法将数组排序,再来判断找出重复元素
    // arrayObject.sort(sortby) 可选参数sortby,必须是函数
    
    function  res(arr){
        var temp = [];
        //  排序之后 相邻对比
        arr.sort().sort(function(a,b){
            if(a === b&temp.indexOf(a)===-1){
                temp.push(a)
            }
        })
        return temp;
    }
    
    var arr = [1,2,3,3,3,3,4,4,5,6,7,7,9,5,4,3,7,7]
    console.log(arr.sort())
    console.log(res(arr));
    
    • 扁平化数组
    • 按数组中各项和特定值差值排序

    26、BOM(Browser Object Model)属性对象方法

    27、服务端渲染

    28、垃圾回收机制

    29、eventloop

    进程和线程
    任务队列

    30、如何快速让字符串变成已千为精度的数字

    相关文章

      网友评论

          本文标题:JS题目

          本文链接:https://www.haomeiwen.com/subject/jrslkctx.html