笔试题

1.下面打印出来的结果(考点 var和let的区别,异步操作)

笔试题_第1张图片

结果:
M1_$8Z4STI3H6WV(OBR]M9J.png

解析
 * 1.for循环结束后的打印:
 我们要明确var和let声明变量的区别:let定义的变量是块级的变量。var定义的变量是全局变量或者函数变量。let定义的变量只对它所在的区域内有效,而var定义的变量范围最少是一个函数之内。
 因此var所定义的变量此时是全局变量,变量提升,for循环结束后还是可以访问到的,又因为for循环是同步的,因此i此时是循环结束的值,为5.
 即使setTimeOut后面的时间设置为0,它还是异步操作,因此执行顺序会在同步操作的后面。
 * 2.for循环中的打印:
  我们要明白for循环执行的顺序是先会执行循环体中的内容再去i++,最后在进行判断,依次循环操作。因此循环之后的结果是i为5.又因为每一次循环i都是同一个i,因此指向的值是相同的,打印出来的结果为 5 5 5 5 5
 又因为setTimeout执行方式采用发布订阅模型,即在当前运行栈注册一个回调,所以当前所有回调几乎同时注册,因此打印出来的时间是相同的

如果是用let声明
笔试题_第2张图片

结果 for循环结束 i访问不到
笔试题_第3张图片

注意
for (let i = 0; i < 3; i++) {
  let i = 'hyx';
  console.log(i); //结果为hyx hyx hyx ,因为设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。这表明函数内部的变量i与循环变量i不在同一个作用域,有各自单独的作用域。
}

2.下面打印出来的结果(考点 var和函数的变量声明提升顺序)

console.log(a,1);
a();
var a=3;
function a(){
    console.log(10,2)
}
console.log(a,3)
a=6;
console.log(a());

结果为
笔试题_第4张图片

实际上真正执行的顺序如下

function a(){       //函数声明
    console.log(10,2)
}
var a;              //变量声明 a为undefined
console.log(a,1);   //结果为function
a();                //结果为10,2
a=3;                //变量a赋值 为3 将函数声明赋值替换掉
console.log(a,3)    //a为number 结果为3 3
a=6;                //变量a赋值 为6 
console.log(a());   //结果是 a is not a function
解析
1.函数声明和变量声明都有声明提升机制;自身的理解是如果命名名称一样的话,函数声明和变量声明都指向同一个区域,函数声明高于变量声明,会把变量声明给替换掉。因为函数声明是整体提升,同时给变量名赋值了。
2.函数声明赋值要早于变量声明赋值;函数声明赋值,是在执行上下文的开始阶段进行的;变量声明赋值,是在执行到赋值语句的时候进行的赋值。(两个的声明都是在预编译阶段进行)

比如:

var a=0;
function f(){
    console.log(a) //undefined
    var a=2; //注意这里使用了var重新命名了变量a,因此存在变量提升,作用域为函数内,因此执行顺序为 var a; console.log(a);a=2;结果为undefined。如果改成a=2;则console.log(a)访问的是全局变量中的a,结果为0.函数内部可以访问函数外部,但函数外部不可以访问函数内部。
}
console.log(a)  //0
f(); //这里才真正执行函数体的内容
结果为0 undefined

3.考点 变量声明和this的取值

function Foo(){
    getName=function (){
        console.log(1)
    }
    return this
}
Foo.getName=function(){
    console.log(2)
}
Foo.prototype.getName=function(){
    console.log(3)
}
var getName=function(){
    console.log(4)
}
function getName(){
    console.log(5)
}
// 请写出以下输出结果:
Foo.getName();
getName();
Foo().getName();
getName();
Foo.getName();
new Foo().getName();
解析为
var getName;              //变量声明
function getName(){       //函数声明
    console.log(5)
}
function Foo(){           //函数声明
    getName=function (){  //这里相当于将全局变量getName重新赋值
        console.log(1)
    }
    return this
}
Foo.getName=function(){   //相当于是给Foo函数新增一个名为getName的属性。
    console.log(2)
}
Foo.prototype.getName=function(){  //相当于是在Foo函数的原型中新增一个名为getName的方法
    console.log(3)
}
getName=function(){        //相当于将全局变量getName重新赋值
    console.log(4)
}

Foo.getName(); //这里的含义是执行Foo的getName属性,结果为2
getName(); //执行全局变量中的getName方法,结果为4
Foo().getName(); //Foo()执行完返回this,转换为this.getName(),谁调用这个函数则this就指向谁,因此当前的this指向的是document文档对象。我们执行Foo(),同时执行了里面的代码,getName被重新赋值 结果为1
getName();//执行全局变量中的getName方法,getName被重新赋值,结果为1
Foo.getName();//这里的含义是执行Foo的getName属性,结果为2
new Foo().getName();//new Foo()相当于创建了一个实例,调用的方法是原型对象中的方法 结果为3

最后的结果为2 4 1 1 2 3

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

String.prototype.trim=function(str){
    if(str && typeof str === "string") {
        return str.replace(/(^\s*)|(\s*)$/g,"");//去除前后空白符
        return str.replace(/\s*/g,"");//去除所有空白符
    }
}

5.typeof的判断

1.
alert(typeof null)   //'object'
alert(typeof undefined)   //'undefined'
alert(typeof NaN)   //'number'
alert(NaN == undefined)   //false
alert(NaN == NaN)   //false,两个NaN是不一样的,但是两个null或者undefined是一样的。null===null
var str'=123abc';
alert(typeof str++)   //'number'  
alert(str)          //NaN

2.
function ClassA (arg){
    this.arg=arg;
}
ClassA.prototype.x=20;  //这里是原型对象中设置内置属性
function ClassB (x){
    this.x=x;
}
ClassB.prototype=new ClassA(30);
var a=new ClassA('xxxxxxx'),b=new ClassB(40);
console.log(a instanceof Object);  //true
console.log(a instanceof ClassA);  //true
console.log(b instanceof Object);  //true
console.log(b instanceof ClassA);  //true
a.x=30; //该实例对象的属性
console.log(a.x); // 30 我们要明白访问的优先级的是先查看该实例是否有该属性名,没有的话再去往原型链上查找。
console.log(a.__proto__.x);// 20  直接访问的原型链
delete a.x; //删除成功 true 删除的是实例对象的属性
console.log(a.x);//20  原型对象的属性x
console.log(b.x);//40 该实例对象的属性x b为{x:40}
delete b.x;//删除成功 true 删除的是实例对象的属性
console.log(b.x);//20  原型对象的属性x
delete ClassA.prototype.x; //删除原型链的属性
console.log(a.x);  //undefined
总结:
区分类型的方法

1.typeof:判断的返回结果是字符串类型,内容是类名.区分不了{},[],null。因为结果都是'object'。适合基本类型及function检测,遇到null失效。

  typeof [] //'object'
  typeof function(){}  //'function'

2.instanceof:返回的值是boolean。
原理就是从当前引用的_proto_一层一层顺着原型链往上找,能否找到对应的prototype。找到了就返回true。
适用于判断一个引用类型是否属于某构造函数;还可以在继承关系中用来判断一个实例是否属于它的父类型。

null instanceof Object  //false
null instanceof null    //报错 这样的写法不对
[] instanceof Array //true
[] instanceof Object //true

3.Object.prototype.toString.apply():适合内置对象,适用于所有类型(在IE678中遇到null和undefined失效。返回[object Object])。

  Object.prototype.toString.call({}) //"[object Object]"
  Object.prototype.toString.call([]) //"[object Array]"
  Object.prototype.toString.call(null) //"[object Null]"
  Object.prototype.toString.call(1) //"[object Number]"
  Object.prototype.toString.call(function(){}) //"[object Function]"
数字类型中的自增或自减 (a++/a--的值就是a的值,而++a/--a的值等于 a+1)

1.前缀
--a相当于 a=a-1
2.后缀
a++的值不变 是等于a的值,但是因为副作用,所以a会自增加一。

   var a=5,b=0;
   b=a++;  //因此 b等于原来a的值 b=5,a=6
   b=++a;  //b=a+1=6+1=7 a=a+1=7
delete操作符的含义

使用delete删除属性时,删除的仅仅是实例对象本身的属性,而不能删除prototype上的属性,即使再删一次也是删除掉不的;若要删除prototype上的属性的属性或方法,只能是:delete ClassA.prototype.x:**
js的内部属性以及delete操作符

6.写一个函数,将对象中属性值为'', undefined, null的属性删除掉

 var obj = {
        name: 'wdd',
        address: {
            code: '',
            tt: null,
            age: 1
        },
        ss: [{
            aa:'',
            cc:null
        }],
        vv: undefined
    }
    function remove(obj) {
        if(obj&& typeof obj =='object'){
            for(var key in obj){
                if(obj[key]==''||obj[key]==null ||obj[key]==undefined){
                    delete obj[key]
                }else if(obj[key]&&typeof obj[key]==='object'){
                    remove(obj[key])
                }
            }
        }else{
            return '传参错误'
        }
        return obj
    }
    var a=remove(obj)
    console.log(a)

7.数组去重

function newArr(arr){
    for(var i=0;i

7.运行下列代码得出结果

function func1(){
    var n=99;
    nAdd=function(){
        this.n+=1;
        console.log(this.n)
    }
    function func2(){
        console.log(n)
    }
    return func2
}
var result=func1();
result(); //99
nAdd();   //NaN  因为nAdd相当于是在全局变量中定义了函数,因此在外面是可以访问到的,此时的this指向window对象,里面this.n为undefined,所以加起来为NaN。
result(); //99

8.promise的执行顺序(哪部分是异步哪部分是同步)

var p=new Promise((resolve)=>{
    console.log(2)
    resolve(3)
    console.log(4)
    resolve(5)
})
p.then((value)=>{
    console.log(value)
})
console.log(1)

结果为2 4 1 3
解析:
1.执行new操作符的时候除了会创建一个实例外,还会执行函数体的语句。
相当于new Promise(()=>{
    //同步操作
    console.log(1)
    resolve()
    reject()
    //状态是p.then().catch()才会运行的,而p.then()是异步操作 
 })
2.在promise中出现多个resolve或者reject状态,promise函数都只会执行一次,并且是执行的第一次

9.Object.defineProperty()用法(双向绑定实现的底层原理)

var a={}
Object.defineProperty(a,'value',{
    get(){console.log(1)},
    set(){console.log(2)}
})
var b=Object.create(a)
console.log(b.value)
b.value=123;
console.log(b.value)

你可能感兴趣的:(前端)