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题目

    JS 1、原型/原型链/构造函数/实例/继承 1. proto(原型) 每个对象又有proto属性,指向创建他的构...

  • js经典题目

    1闭包 链接:学习Javascript闭包(Closure) setTimeout在js单线程中只是放在队列中并未...

  • js题目练习

    7.23 1,计算给定数组arr中所有元素的总和,数组中的元素均为Number 类型。例如:[1,2,3,4]--...

  • js数组题目

    1、寻找两个数组中相同的元素中最小的元素 2、判断一个字符串中出现次数最多的字符,统计这个次数var str=’a...

  • js数组题目

    js面试题 js数组 一、按要求分割数组 将"js,数组,分类"字符串数组以/分割 for循环累加 join()把...

  • 有趣的js题目

    中二分法 从已经排好顺序的数组中取出一个数的位置(也可能是插入一个数到排列好的数组中而不打乱顺序) 最大公约数 1...

  • Js常见题目2

    移动端是怎么做适配的? 1.使用 标签包含 name、 content...

  • Js常见题目3

    3.用过CSS3吗? 实现圆角矩形和阴影怎么做? 圆角border-radius; http://js.jiren...

  • Js常见题目1

    1.请写出一个符合 W3C 规范的 HTML 文件,要求 页面标题为「我的页面」 页面中引入了一个外部 CSS 文...

  • js的部分题目

网友评论

      本文标题:JS题目

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