面试题总结

161 阅读7分钟

js基础

1.原始类型有哪几种?null 是对象嘛?

string,number,bollean,null,nudefined,symbol

原始类型存放的是值,没有函数可以调用。

null不是对象但是typeof null会输出object这是js悠久的bug

2.对象类型:

原始类型和对象类型不同:原始类型存放的是值,对象类型存储的指针

当创建一个对象类型的时候,计算机会在内存中开辟一个空间存放值,但是我们找到这个空间,这个空间需要一个地址(指针);
    const a = []
    const b = a
    b.push(1),
当我们把一个变量赋值给另外一个变量的时候,复制的是原始变量的指针,当我们进行数据修改的时候,就会修改存放在地址(指针) #001上的值,也就导致了两个变量的值都发生了改变。
function test(person) {
  person.age = 26
  person = {
    name: 'yyy',
    age: 30
  }

  return person
}
const p1 = {
  name: 'wgl',
  age: 25
}
const p2 = test(p1)
console.log(p1) // -> ?
console.log(p2) // -> ?

1.首先,函数传参是传递对象指针的副本

2.到函数内部修改参数的属性这步, 当前p1 的值也被修改了

3.当到了person重新分配的时候指针发生了变化

最终输出结果为:{name:'wgl',age:'26'}/{name:'yyy',age:30}

3.判断数据类型的几种方法:

1.typeof

返回的数据类型包括:number、boolean、symbol、string、object、undefined、function。
typeof null 返回数据类型错误 返回object
引用类型除了function返回function,其余都是返回object

2.tostring

tostring是Object原型上面的方法,调用该方法,默认返回当前对象的[[Class]],这是一个内部属性,其格式为[Object xxx]对于object对象可以直接调用tostring方法,但是对于其他的就需要借助 call/apply
Object.prototype.toString.call('') ;   // [object String]
Object.prototype.toString.call(1) ;    // [object Number]
Object.prototype.toString.call(true) ; // [object Boolean]
Object.prototype.toString.call(Symbol()); //[object Symbol]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
Object.prototype.toString.call(new Error()) ; // [object Error]
Object.prototype.toString.call(document) ; // [object HTMLDocument]
Object.prototype.toString.call(window) ; //[object global] window 是全局对象 global 的引用

3.instanceof

instanceof 是用来判断 A 是否为 B 的实例,表达式为:A instanceof B,如果 A 是 B 的实例,则返回 true,否则返回 false。 在这里需要特别注意的是:instanceof 检测的是原型,

由上图可以看出[]的原型指向Array.prototype,间接指向Object.prototype, 因此 [] instanceof Array 返回true, [] instanceof Object 也返回true。

instanceof 只能用来判断两个对象是否属于实例关系, 而不能判断一个对象实例具体属于哪种类型。

console.log(1 instanceof Number);                    // false
console.log(true instanceof Boolean);                // false 
console.log('str' instanceof String);                // false  
console.log([] instanceof Array);                    // true
console.log(function(){} instanceof Function);       // true
console.log({} instanceof Object);                   // true

4.原型与原型链

Prototype 原型

function Foo(){}

首先介绍 Prototype属性 ,这是一个显示原型属性,只有函数拥有该属性,当函数被创建的时候就产生了这个属性,并且这个属性的值是一个对象,只有一个属性 constructor {constructor:f},constructor对应着构造函数(Foo),它是一个公有且不可枚举的属性,一旦我们改变了函数的prototype,那么新对象就没有constructor属性了(但是可以通过原型链取到constructor)作用:1.让实例对象知道是什么函数构造了它 2.如果想给某些类库中的构造函数增加一些自定义的方法就可以通过xxx.constructor.methed来扩展

proto

每个对象都有隐式原型属性,指向创建该对象的的构造函数的原型。因为js中没有类的概念,为了实现类似继承的方,通过__proto__将对象和原型联系起来组成原型链,得以让对象访问不属于自己的属性。

如何产生:当我们使用new操作符的时候,生成的实例对象拥有了__proto__属性。可以理解为在new的过程中,新对象被添加了__proto__属性链接到了构造函数的原型上。

new的过程

1.新生成了一个对象

2.链接到原型

3.绑定this并执行构造函数

4.返回新的对象

function new(){
    //创建一个空的对象
    let obj = new Object();
    //获得构造函数
    let Con = [].shift.call()arguments;
    //链接到原型
    obj.__proto__ = Con.prototype;
    //绑定this执行构造函数
    let result = Con.apply(obj,arguments);
    //确保new出来是个对象
    return typeof result == 'object'?result:obj;
}

总结

1.Object 是所有对象的爸爸,所有对象都可以通过 proto 找到它

2.Function 是所有函数的爸爸,所有函数都可以通过 proto 找到它

3.Function.prototype 和 Object.prototype 是两个特殊的对象,他们由引擎来创建 除了以上两个特殊对象,其他对象都是通过构造器 new 出来的

4.函数的 prototype 是一个对象,也就是原型

5.对象的 proto 指向原型, proto 将对象和原型连接起来组成了原型链

__let const var__区别

  • var声明的变量会挂载到window 而var 不会
  • var 声明的变量会提升 let const 不会
  • let const 会形成块级作用域
  • 统一作用域下 let const 不能声明同名变量 而var可以

js进阶

for(var i=0;i<5;i++){
    setTimeout(function({
        console.log(i)
    },1000)
}

输出结果为:5,5,5,5,5

解析:for 循环会先执行完(同步优先于异步优先于回调)setTimeout是异步执行的,1000毫秒后向任务队列里添加一个任务,只有主线上的全部执行完才会执行任务队列里的任务,所以当主线程for循环执行完之后 i的值为5,这个时候再去任务队列中执行任务,i全部为5;

每次for循环的时候setTimeout都会执行,但是里面的function则不会执行被放入任务队列,因此放了5次;for循环的5次执行完之后不到1000毫秒;

因为 setTimeout 的 console.log(i); 的i是 var 定义的,所以是函数级的作用域,不属于 for 循环体,属于 global。等到 for 循环结束,i 已经等于 5 了,这个时候再执行 setTimeout 的五个回调函数(参考上面对事件机制的阐述),里面的 console.log(i); 的 i 去向上找作用域,只能找到 global下 的 i,即 5。所以输出都是 5。

如何修改后输出01234

1.方式一 使用自执行函数

for (var i = 0; i < 5; i++) { 
  (function(i){   //立刻执行函数
    setTimeout(function (){
      console.log(i); 
     },1000); 
  })(i); 
}

解析:这里用到立刻执行函数。这样 console.log(i); 中的i就保存在每一次循环生成的立刻执行函数中的作用域里了。

2.方式二

for(let i=0;i<5;i++){
    setTimeout(function({
        console.log(i)
    },1000)
}

解析:let 为代码块的作用域,所以每一次 for 循环,console.log(i); 都引用到 for 代码块作用域下的i,因为这样被引用,所以 for 循环结束后,这些作用域在 setTimeout 未执行前都不会被释放。

3.方式三

var output = function(i){
    setTimeout(function(){
        console.log(i)
    },1000)//闭包
},
for(var i=0;i<5;i++){
    output(i);
}

如何输出012345

1.方式一

for(var i=0;i<5;i++){
    (function(j){
        setTimeout(function(){
            console.log(j)
        },1000*j)
    })(i)
}
setTimeout(function(){
    console.log(i)
},1000*i)

2.方式二:

var task = [];
for(var i=0;i<5;i++){
    (function(j){
        setTimeout(function(){
            task.push(new Promise(resolve=>{
                console.log(j)
                resolve()
            }))
        },1000*j)
    })(i)
}
Pormise.all(task).then(data=>{
   console.log(i)
})

笔试

var n = 123;
function fn1(){
    console.log(n);//所以这里面的n指的的全局作用域的n
}
function fn2(){
    var n = 456
    fn1(n)//无调用者 等价于 window.fn1()
}
fn2()//无调用者  等价于 window.fn2()
console.log(n)
最终输出结果为:123,123
var length = 100;
function f1(){
    console.log(this.length);
}
var obj = {
    x:10,
    fn2:function(f1){
        f1();//无调用者 this指向window  输出100
        arguments[0]();//无调用者 等价于 arguments.f1();this指向arguments这个对象
    }
}
obj.fn2(f1,1)
function fn1(){
    console.log(this.a)
}
var obj = {
    a:1,
    f:f
}
var f2 = obj.f;
var a = 'hello';
f2();// obj.f 没有执行,只是赋值,这里f2()无调用者 this指向window最终输出 hello