JavaScript题解

1.实现一个方法,使得:add(2, 5) 和 add(2)(5) 的结果都为 7

var add = function (x, r) {
    if (arguments.length === 1) {
        return function (y) { return x + y; };
    } else {
        return x + r;
    }
};
console.log(add(2)(5));  // 7
console.log(add(2, 5));  // 7

2.alert(1 && 2) 和 alert(1 || 0) 的结果是

alert(1 && 2 ) 的结果是 2

  • 只要 "&&" 前面是 false,无论 "&&" 后面是 true 还是 false,结果都将返 "&&" 前面的值
  • 只要 "&&" 前面是 true,无论 "&&" 后面是 true 还是 false,结果都将返 "&&" 后面的值

alert(0 || 1) 的结果是 1

  • 只要 "||" 前面为 false,不管 "||" 后面是 true 还是 false,都返回 "||" 后面的值
  • 只要 "||" 前面为 true,不管 "||" 后面是 true 还是 false,都返回 "||" 前面的值

只要记住 0 与 任何数都是 0,其他反推

3.下面的输出结果是 (考点:作用域、运算符(赋值预算,逗号运算))

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)());

结果:25,20,20,25

先看第一个输出:25,因为 ( inner.func, inner.func ) 是进行逗号运算符,逗号运算符就是运算前面的 ”,“ 返回最后一个,举个栗子:

var i = 0, j = 1, k = 2;
console.log((i++, j++, k)) // 返回的是 k 的值 2 ,如果写成 k++ 的话  这里返回的就是 3
console.log(i); // 1
console.log(j); // 2
console.log(k); // 2   

回到原题 ( inner.func, inner.func ) 就是返回 inner.func ,而 inner.func 只是一个匿名函数

function () {
    var out = 30;
    return this.out;
}

而且这个匿名函数是属于 window 的,则变成了

(function () {
    var out = 30;
    return this.out;
})()

此刻的 this => window,所以 out 是 25

第二和第三个 console.log 的作用域都是 inner,也就是他们执行的其实是 inner.func(); inner 作用域中是有 out 变量的,所以结果是 20

第四个 console.log 考查的是一个等号运算 inner.func = inner.func ,其实返回的是运算的结果, 举个栗子:

var a = 2, b = 3;
console.log(a = b) // 输出的是 3

所以 inner.func = inner.func 返回的也是一个匿名函数

function () {
    var out = 30;
    return this.out;
}

此刻,道理就和第一个 console.log 一样了,输出的结果是 25

4.下面程序输出的结果是(考点:变量提升)

if (!("a" in window)) {
    var a = 1;
}
alert(a);

代码解析:如果 window 不包含属性 a,就声明一个变量 a,然后赋值为 1

es6 之前,所有的全局变量都是 window 的属性,语句 var a = 1; 等价于 window.a = 1; 你可以用如下方式来检测全局变量是否声明:"变量名称" in window 所有的变量声明都在范围作用域的顶部,看一下相似的例子:

alert("b" in window);
var b;

此时,尽管声明是在 alert 之后,alert 弹出的依然是 true,这是因为 JavaScript 引擎首先会扫描所有的变量声明,然后将这些变量声明移动到顶部,最终的代码效果是这样的:

var b;
alert("b" in window);

但是,你需要理解该题目的意思是,变量声明被提前了(预编译),但变量赋值没有,因为这行代码包括了变量声明和变量赋值;你可以将语句拆分为如下代码:

var a;    //声明
a = 1;    //初始化赋值

当变量声明和赋值在一起用的时候,JavaScript 引擎会自动将它分为两部以便将变量声明提前, 不将赋值的步骤提前,是因为他有可能影响代码执行出不可预期的结果 知道了这些概念以后,重新回头看一下题目的代码,其实就等价于:

var a;
if (!("a" in window)) {
    a = 1;
}
alert(a);

题目的意思就非常清楚了:首先声明 a,然后判断 a 是否在存在,如果不存在就赋值为1,很明显 a 永远在 window 里存在,这个赋值语句永远不会执行,所以结果是 undefined

5.下面程序输出的结果是 (考点:变量函数声明)

var a = 1;
var b = function a(x) {
  x && a(--x);
};
alert(a);

结果:1 这里依然有 3 个重要的概念需要我们知道:

  • 第一个是变量声明在进入执行上下文就完成了
  • 第二个概念就是函数声明也是提前的,所有的函数声明都在执行代码之前都已经完成了声明,和变量声明一样
    • 函数声明是如下这样的代码:
    function functionName(arg1, arg2){
        //函数体
    }
    
    • 如下不是函数,而是函数表达式,相当于变量赋值:
    var functionName = function(arg1, arg2){
       //函数体
    };
    ``
    

函数表达式没有提前,就相当于平时的变量赋值

  • 第三需要知道的是,函数声明会覆盖变量声明,但不会覆盖变量赋值

举个栗子:

function value(){
    return 1;
}
var value;
alert(typeof value);    //"function"

尽管变量声明在下面定义,但是变量 value 依然是 function,也就是说这种情况下,函数声明的优先级高于变量声明的优先级,但如果该变量 value 赋值了,那结果就完全不一样了:

function value(){
    return 1;
}
var value = 1;
alert(typeof value);    //"number"

value 赋值以后,变量赋值初始化就覆盖了函数声明

6.闭包

想每次点击对应目标时弹出对应的数字下标 0~4 ,但实际是无论点击哪个目标都会弹出数字 5

function onMyLoad() {
    var arr = document.getElementsByTagName("p");
    for (var i = 0; i < arr.length; i++) {
        arr[i].onclick = function () {
            alert(i);
        }
    }
}
  1. 加若干个对应的闭包域空间(这里采用的是匿名函数),专门用来存储原先需要引用的内容(下标),不过只限于基本类型(基本类型值传递,对象类型引用传递)
//声明一个匿名函数,若传进来的是基本类型则为值传递,故不会对实参产生影响,
//该函数对象有一个本地私有变量 arg(形参) ,该函数的 function scope 的 closure 对象属性有两个引用,一个是 arr,一个是 i
//尽管引用 i 的值随外部改变 ,但本地私有变量(形参) arg 不会受影响,其值在一开始被调用的时候就决定了
for (var i = 0; i < arr.length; i++) {
  (function (arg) {
    arr[i].onclick = function () {
      // onclick 函数实例的 function scope 的 closure 对象属性有一个引用 arg,
      alert(arg);
      //只要 外部空间的 arg 不变,这里的引用值当然不会改变
    }
  })(i); //立刻执行该匿名函数,传递下标 i (实参)
}
  1. 将事件绑定在新增的匿名函数返回的函数上,此时绑定的函数中的 function scope 中的 closure 对象的 引用 arg 是指向将其返回的匿名函数的私有变量 arg
for (var i = 0; i < arr.length; i++) {
  arr[i].onclick = (function (arg) {
    return function () {
      alert(arg);
    }
  })(i);
}
  1. 使用 ES6 新语法 let 关键字
for (var i = 0; i < arr.length; i++) {
  let j = i; // 创建一个块级变量
  arr[i].onclick = function () {
    alert(j);
  }
}

7.使用 JavaScript 生成随机数,但不能返回0

function randomFraction() {
    var result = 0;
    while (result === 0) {
        result = Math.random();
    }
    return result;
}

8.使用箭头函数的语法来计算squaredIntegers数组里正整数的平方(分数不是整数)

const realNumberArray = [4, 5.6, -9.8, 3.14, 42, 6, 8.34];
const squareList = (arr) => {
    "use strict";
    const squaredIntegers = arr.filter((item) => Math.floor(item) == item).map((item) => item * item)
    return squaredIntegers;

};
const squaredIntegers = squareList(realNumberArray);
console.log(squaredIntegers);

9.用JS递归的方式写1到100求和

  function sum(n){
    if(n==1) return 1;
    return sum(n-1) + n;
  }
  var amount = sum(100);
  console.log(amount); // 5050

10.用 JavaScript 写一个函数,输入 int 型,返回整数逆序后的字符串。

如:输入整型 1234,返回字符串“4321”。要求必须使用递归函数调用,不能用全局变量,输入函数必须只有一个参数传入,必须返回字符串。

function test(num) {
  var str = num + "";
  if (str.length > 1) {
    var newStr = str.substring(str.length - 1);
    var oldStr = str.substring(0, str.length - 1);
    return newStr + test(oldStr);
  } else {
    return str;
  }
}

console.log(test(123));

11.如何把一个字符串的大小写取反(大写变小写小写变大写),例如 ’AbC' 变成 'aBc'

// 方法一:常规
function transformStr(str) {
    let tempArr = str.split('')
    let result = tempArr.map(char => {
        return char === char.toUpperCase() ? char.toLowerCase() : char.toUpperCase()
    })
    return result.join('')
}
console.log(transformStr('aBc'))

// 方法二:正则
'aBc'.replace(/[A-Za-z]/g, char => char === char.toUpperCase() ? char.toLowerCase() : char.toUpperCase())