JavaScript编程题

LeslieLin 编程技术
阅读量 0 评论量 0

1. 写出下面这段代码的输出结果是?

var out = 25,
    inner = {
        out : 20,
        func: function () {
            var out = 30;
            return this.out;
        }
    }
console.log((inner.func, inner.func)());
console.log(inner.func());
console.log((inner.func)());
console.log((inner.func = inner.func)());

输出结果 :

  1. 25
  2. 20
  3. 20
  4. 25

解析:

本题考点两个: 1, 作用域;2. 运算符(赋值运算符,逗号运算符)

第一个考的就是逗号运算符,逗号运算符就是运算前面的,返回最后一个;例如:var i= 1;j = 2; k = 5; console.log((i++, j++, k)),最后输出的是5;回到题目,逗号运算符会返回inner.func,而inner.func是一个匿名函数,而这个匿名函数是属于window的,所以里面的this指向的是window,输出25;

第二,第三个输出都是20,因为this指向的是inner

最后一个考的是等号运算符,inner.func = inner.func其实返回的是运算的结果,也就是返回的是匿名函数,所以this也是指向window,输出的是25

2. 以下代码片段,输出的结果是?简要解释一下。

var name = '1';
var object = {
    name: '2',
    getNameFunc: function () {
        return function () {
            return this.name;
        }
    }
}
console.log(object.getNameFunc()())

输出结果: 1

解释:

object.getNameFunc()执行完后,返回的是一个匿名函数,所以里面的this指向的就是window,最终结果输出的是1;

3. 下面代码的输出是什么?

function sayHi() {
    console.log(name);
    console.log(age);
    var name = '张三';
    let age = 21;
}
sayHi();

答案: undefinedReferenceError

在函数中,我们首先使用var关键字声明了name变量。 这意味着变量在创建阶段会被提升(JavaScript会在创建变量创建阶段为其分配内存空间),默认值为undefined,直到我们实际执行到使用该变量的行。 我们还没有为name变量赋值,所以它仍然保持undefined的值。

使用let关键字(和const)声明的变量也会存在变量提升,但与var不同,初始化没有被提升。 在我们声明(初始化)它们之前,它们是不可访问的。 这被称为“暂时死区”。 当我们在声明变量之前尝试访问变量时,JavaScript会抛出一个ReferenceError

4. 下面代码输出是什么?

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 1);
}

for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 1);
}

答案:3 3 3 and 0 1 2

由于JavaScript中的事件执行机制,setTimeout函数真正被执行时,循环已经走完。 由于第一个循环中的变量i是使用var关键字声明的,因此该值是全局的。 在循环期间,我们每次使用一元运算符++都会将i的值增加1。 因此在第一个例子中,当调用setTimeout函数时,i已经被赋值为3
在第二个循环中,使用let关键字声明变量i:使用let(和const)关键字声明的变量是具有块作用域的(块是{}之间的任何东西)。 在每次迭代期间,i将被创建为一个新值,并且每个值都会存在于循环内的块级作用域。

5. 下面代码的输出是什么?

const shape = {
  radius: 10,
  diameter() {
    return this.radius * 2;
  },
  perimeter: () => 2 * Math.PI * this.radius
};

shape.diameter();
shape.perimeter();

答案: 20 and NaN

请注意,diameter是普通函数,而perimeter是箭头函数。
对于箭头函数,this关键字指向是它所在上下文(定义时的位置)的环境,与普通函数不同! 这意味着当我们调用perimeter时,它不是指向shape对象,而是指其定义时的环境(window)。没有值radius属性,返回undefined

6. 下面代码的输出是什么?

+true;
!"张三";

答案:1 and false

一元加号会尝试将boolean类型转换为数字类型。 true被转换为1false被转换为0

字符串'Lydia'是一个真值。 我们实际上要问的是“这个真值是假的吗?”。 这会返回false

7、如何判断一个对象是否为数组

function isArray(arg) {
    if (typeof arg === 'object') {
        return Object.prototype.toString.call(arg) === '[object Array]';
    }
    return false;
}

8、冒泡排序

  • 每次比较相邻的两个数,如果后一个比前一个小,换位置
var arr = [3, 1, 4, 6, 5, 7, 2];

function bubbleSort(arr) {
for (var i = 0; i < arr.length - 1; i++) {
    for(var j = 0; j < arr.length - 1; j++) {
        if(arr[j + 1] < arr[j]) {
            var temp;
            temp = arr[j];
            arr[j] = arr[j + 1];
            arr[j + 1] = temp;
        }
    }
}
return arr;
}

console.log(bubbleSort(arr));

9、快速排序

  • 采用二分法,取出中间数,数组每次和中间数比较,小的放到左边,大的放到右边
var arr = [3, 1, 4, 6, 5, 7, 2];

function quickSort(arr) {
    if(arr.length == 0) {
        return [];    // 返回空数组
    }

    var cIndex = Math.floor(arr.length / 2);
    var c = arr.splice(cIndex, 1);
    var l = [];
    var r = [];

    for (var i = 0; i < arr.length; i++) {
        if(arr[i] < c) {
            l.push(arr[i]);
        } else {
            r.push(arr[i]);
        }
    }

    return quickSort(l).concat(c, quickSort(r));
}

console.log(quickSort(arr));

10. 下面的代码输出什么?

const promise = new Promise((resolve, reject) => {
    console.log(1);
    resolve();
    console.log(2);
})

promise.then(() => {
    console.log(3);
})

console.log(4);

解析

首先 Promise 新建后立即执行,所以会先输出 1,2,而 Promise.then() 内部的代码在 当次 事件循环的 结尾 立刻执行 ,所以会继续输出4,最后输出3。

答案

1
2
4
3

11. 下面的代码输出什么?

const promise = new Promise((resolve, reject) => {
    resolve('success1');
    reject('error');
    resolve('success2');
});

promise.then((res) => {
    console.log('then:', res);
}).catch((err) => {
    console.log('catch:', err);
})

解析

resolve 函数将 Promise 对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;

reject 函数将 Promise 对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

而一旦状态改变,就不会再变。 所以 代码中的reject('error'); 不会有作用。

Promise 只能 resolve 一次,剩下的调用都会被忽略。 所以 第二次的 resolve('success2'); 也不会有作用。

答案

then: success1

12. 下面代码输出什么?

Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .then(console.log)

解析

Promise.resolve 方法的参数如果是一个原始值,或者是一个不具有 then 方法的对象,则 Promise.resolve 方法返回一个新的 Promise 对象,状态为resolvedPromise.resolve 方法的参数,会同时传给回调函数。

then 方法接受的参数是函数,而如果传递的并非是一个函数,它实际上会将其解释为 then(null),这就会导致前一个 Promise 的结果会穿透下面。

Promise.resolve(1)
  .then(null) // 穿透
  .then(null) // 穿透
  .then(console.log)

答案

1

13. 实现需求

红灯三秒亮一次,绿灯一秒亮一次,黄灯2秒亮一次;如何让三个灯不断交替重复亮灯?(用Promse实现)三个亮灯函数已经存在:

function red() {
    console.log('red');
}
function green() {
    console.log('green');
}
function yellow() {
    console.log('yellow');
}

解析

红灯三秒亮一次,绿灯一秒亮一次,黄灯2秒亮一次,意思就是3秒,执行一次 red 函数,2秒执行一次 green 函数,1秒执行一次 yellow 函数,不断交替重复亮灯,意思就是按照这个顺序一直执行这3个函数,这步可以就利用递归来实现。

答案

function red() {
    console.log('red');
}
function green() {
    console.log('green');
}
function yellow() {
    console.log('yellow');
}

var light = function (timmer, cb) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            cb();
            resolve();
        }, timmer);
    });
};

var step = function () {
    Promise.resolve().then(function () {
        return light(3000, red);
    }).then(function () {
        return light(2000, green);
    }).then(function () {
        return light(1000, yellow);
    }).then(function () {
        step();
    });
}

step();

14. 以下代码最后输出什么?

const first = () => (new Promise((resolve, reject) => {
    console.log(3);
    let p = new Promise((resolve, reject) => {
        console.log(7);
        setTimeout(() => {
            console.log(5);
            resolve(6);
        }, 0)
        resolve(1);
    });
    resolve(2);
    p.then((arg) => {
        console.log(arg);
    });

}));

first().then((arg) => {
    console.log(arg);
});
console.log(4);

解析

这道题就其实和 Promise 的关系不太大,主要是需要理解 JS执行机制,才能很好的解决这道题。

第一轮事件循环

先执行宏任务,主script ,new Promise立即执行,输出【3】,
执行 p 这个new Promise 操作,输出【7】,
发现 setTimeout,将回调放入下一轮任务队列(Event Queue),p 的 then,姑且叫做 then1,放入微任务队列,发现 first 的 then,叫 then2,放入微任务队列。执行console.log(4),输出【4】,宏任务执行结束。
再执行微任务,执行 then1,输出【1】,
执行 then2,输出【2】。
到此为止,第一轮事件循环结束。开始执行第二轮。

第二轮事件循环

先执行宏任务里面的,也就是 setTimeout 的回调,输出【5】。
resolve(6) 不会生效,因为 p 这个 Promise 的状态一旦改变就不会在改变了。

答案

3
7
4
1
2
5 

15、js 统计一个字符串出现频率最高的字母/数字

let str = 'asdfghjklaqwertyuiopiaia';
const strChar = str => {
    let string = [...str],
        maxValue = '',
        obj = {},
        max = 0;
    string.forEach(value => {
        obj[value] = obj[value] == undefined ? 1 : obj[value] + 1
        if (obj[value] > max) {
            max = obj[value]
            maxValue = value
        }
    })
return maxValue;
}
console.log(strChar(str))    // a

16、不借助临时变量,进行两个整数的交换

let a = 2,
    b = 3;
    [b,a] = [a,b]
    console.log(a,b)   // 3 2
喵~