前端面试总结/JS面试

1.JS继承机制 
构造函数中的属性和方法是不共享的,而为了共享一些属性和方法,也为了不浪费资源,出现了prototype,挂在prototype上的属性和方法是共享的。

function Ani(name) {
    this.name = name;
    // this.act = '叫';
}

var dog = new Ani('狗');
var cat = new Ani('猫');

// dog.act = '旺旺'; //只修改了dog的act属性  不会影响cat的act属性
Ani.prototype.act = '旺旺';

console.log(dog.name, dog.act, cat.name, cat.act);

由此想到原型链继承: 
题目:用原型链的方式实现继承。创建一个基类PeoClass,其中有两个公开方法:eat、sleep, 再创建一个子类EngiClass, eat继承基类,重写sleep方法:

function PeoClass() {

}
PeoClass.prototype = {
    eat: function() {

    },
    sleep: function() {

    }
}

function EngiClass() {

}
EngiClass.prototype = new PeoClass();
EngiClass.prototype.constructor = EngiClass;
EngiClass.prototype.sleep= function() {
    // 重写
}

2.关于深拷贝

function deepClone(obj){
    var newObj = obj instanceof Array ? [] : {};
    for(var i in obj){
       newObj[i] = typeof obj[i] == 'object'? deepClone(obj[i]) : obj[i];    
    }
    return newObj;
}

3.关于数组 
(1)数组去重

function norepeat(arr) {
    var new_arr = [];
    var obj = {};

    for(var i = 0, len = arr.length; i < len; i ++) {
        if(!obj[arr[i]]) {
            obj[arr[i]] = arr[i];
            new_arr.push(i);
        }
    }
    return new_arr; 
}

(2)数组排序/乱序

function arrRand(arr) {
    arr.sort(function() {
        return 0.5 - Math.random();
    });
}

(3)出现次数

function showCount(param) {
    param = typeof param == 'string' ? param.split('') : param;

    var obj = {};

    for(var i = 0, len = param.length; i < len; i ++) {
        if(!obj[param[i]]) {
            obj[param[i]] = 1;
        }else {
            obj[param[i]] += 1;
        }
    }

    return obj;
}

4.关于作用域、闭包以及this 
作用域:javascript是基于词法作用域的语言,也就是静态作用域,即在函数定义时就确定了作用域,而不是执行时 
闭包:两个函数中间包裹一块无法直接访问的作用域,只能通过间接地方式访问;闭包可以使内部变量始终保存在内存中;有闭包就可能造成内存泄漏。

function f1() {
    var n = 99;

    add = function() {
        n += 1;
    }
    function f2() {
        console.log(n);
    }
    return f2;
}
var res = f1();
res(); // 99
add();
res(); // 100  n一直保持在内存中
/************************************************/
var x = 20;
var a = {
    x: 15,
    fn: function() {
        var x = 30;
        return function() {
            return x;
        };
    }
}
console.log(a.fn());// function() {return x}
console.log(a.fn()());// 30
console.log((a.fn())());// 30
console.log((a.fn())() == a.fn()());// true
console.log(a.fn().call(this));// 
console.log(a.fn().call(a)); //

关于this:动态绑定,也就是在函数执行时从上下文中获取指向,上下文不同,this指向不同;一般指向调用者对象,也就是函数的拥有者;特殊的情况是自执行函数,this指向window;(闭包遇上this时,要注意运行时的上下文,看栗子)(当构造函数中return一个对象(除了null)时,this指向return的对象)

var x = 'windows';
var obj_a = {
    x: 1,
    y: {
        x: 2,
        y: function() {
            return function() {
                return this.x;
            }
        },
        z: function() {
            return this.x;
        }
    }
}

var fun = obj_a.y.y();
console.log(obj_a.y.z()); // 2
console.log(obj_a.y.y()()); // windows
console.log(fun()); // 'windows'

4.关于call/apply/bind

var obj = {
    a: 'obj.a',
    b: function() {
        console.log(this.a);
    }
}
var fun = obj.b;
fun(); // undefined 因为执行时上下文中的this指向window
obj.b(); // 'obj.a' 执行时上下文中的this指向obj

call:改变对象的环境,即改变this指向并调用函数;参数:n个,第一个参数是目标对象或者对象本身,其他参数可以是原函数的参数; 
上述fun()语句若改为

 fun.call(obj);//'obj.a'上下文中原本指向window的this强制指向了obj对象

apply:与call方法类似,效果也类似,只是参数格式不同,第一个参数都是目标对象或对象本身,但第二个参数必须是个数组,放置函数的参数

注意:非严格模式下,若call/apply的第一个参数传入null、undefined, 则this指向window对象

bind:与以上两个方法有些不同,但是都能改变this指向,不同之处在于bind方法并不能调用原函数,而是返回一个修改后的函数,称为绑定函数,若要执行需要手动调用该函数;

fun.bind(obj)(); // 'obj.a'

同样bind也可以有多个参数,格式可以与call一样,也可以执行时再次添加

var a = {
    x: 'mx',
    y: function(a, b) {
        console.log(this.x);
        console.log(a+b);
    }
}
var b = a.y;
b.call(a, 1, 2); // mx 3
b.apply(a, [1, 2]); // mx 3
b.bind(a, 1, 2)() // mx 3
b.bind(a, 1)(2) // mx 3 

猜你喜欢

转载自blog.csdn.net/qq_31965515/article/details/80177827