笔试总结


JavaScript 面试知识点总结

以下选项中有变量提升的定义方式是

A、 const num = 0
B、 function num(){}
C、 var num = 0
D、 num = 0

Hoisting(变量提升) 意味着变量和函数的 声明 会在物理层面移动到代码的最前面,但这么说并不准确。实际上变量和函数声明在代码里的位置是不会动的,而是在编译阶段被放入内存中。

详解

BC

var hoisting

Function declaration hoisting

Class declarations are not hoisted

1. What’s the output?

var a = 10;
sayHi();
function sayHi() {
  a = a + 10;
  console.log(a);
  return a;
}
console.log(a);
console.log(sayHi() + 10);
解析
20
30
40

// 申明全局 a, 值为 10
var a = 10;
sayHi(); // 调用
function sayHi() {
  a = a + 10; // 这里的 a 是全局的 a, 而不是局部的,所以执行的结果是把全局 a 设置成了 20
  console.log(a); //20
  return a; // 返回 20
}
console.log(a); // 全局 a 已经是 20 了
console.log(sayHi() + 10);
// 首先又一次执行了 sayHi(), 结果把 a 改成了 30,
// 然后打印了一次 30, 执行完以后返回了 30, 然后再加 10, 打印结果 40

2. What’s the output?

var a = 10;
sayHi();
function sayHi() {
  var a = a + 10;
  console.log(a);
  return a;
}
console.log(a);
console.log(sayHi() + 10);
解析
NaN
10
NaN

// 申明全局 a, 值为 10
var a = 10;
// 调用
sayHi();
function sayHi() {
  // 注意这里有一个 var, 那么这里的 a 就是局部变量了,
  // 另外还是需要预解析一下,其实可以这么去看代码:
  /*
    var a;  // 申明未赋值,默认值是 undefined
    a = a + 10;  // a = undefined + 10 结果是 NaN
    */
  var a = a + 10;
  console.log(a); // NaN
  return a; // 返回 NaN
}
console.log(a); // 这个还是全局的 a, 所以结果 10
console.log(sayHi() + 10);
// 依据上面的分析,这里的 sayHi 会打印一次 NaN,
// 然后加 10, 结果还是 NaN

在函数内部不带 var 声明变量则表示该变量为全局变量,否则该变量为局部变量。


3. What’s the output?

console.log(['1', '2', '3'].map(parseInt));

parseInt()

解析
[1, NaN, NaN]

parseInt() 支持两个参数 (val, radix), 其中 radix 表示要解析的数字的基数。(该值介于 2 ~ 36 之间,并且字符串中的数字不能大于 radix 才能正确返回数字结果值).

Array.prototype.map() 支持 3 个参数 (element, index),因此三次传入的参数分别为 “1-0”, “2-1”, “3-2”

因为字符串的值不能大于基数,因此后面两次调用均返回 NaN , 第一次基数为 0 , 按十进制解析返回 1.


4. What’s the output?

const add = (m) => {
  const temp = (n) => add(m + n);
  temp.toString = () => m.toString(2);
  return temp;
};

console.log(add(3)(4)(5));
解析

参考解析:

1100 # 二进制
  1. 首先执行 add(3), 这个时候需要注意的是根据参数创建了一个局部的 m 变量值为 3, 但是返回值是一个 temp 的函数,也就是说 add(3) 的结果是 temp 函数,继续后面执行:temp(4), 这个函数接收一个 n 参数,现在的值是 4, 然后调用 add(m + n), 受到闭包影响,这里的 m 其实就是第一次的 addm, 也就是 3, 所以这里其实就 add(3 + 4), 也就是 add(7), 执行后返回的这次 add 中创建的 temp, 那么后面就是 temp(5) 了,根据上面的推导,执行后的结果其实 add(7 + 5), 也就是最后一次调用 add 的时候传入的是 12, 也就是 add(12), 这个再次返回 temp, 当 console.log 的时候,会默认调用 toString 方法进行字符串格式化,因为 temptoString 被重写了,那么打印的其实 m.toString(2), 也就是 (12).toString(2), 打印 12 的二进制值,也就是 1100 了。

Number.prototype.toString() - JavaScript | MDN


🔥 5. What’s the output?

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();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
解析
Foo.getName(); // 2
getName(); // 4
Foo().getName(); // 1
getName(); // 1
new Foo.getName(); // 2
new Foo().getName(); // 3
new new Foo().getName(); // 3

6. What’s the output?

if (-1) {
  console.log('yes');
} else {
  connsole.log('no');
}
解析
yes

7. What’s the output?

var num = 1;
(function () {
  console.log(num);
  var num = 2;
})();
解析
undefined

8. What’s the output?

console.log(typeof new Boolean('a'));
console.log(typeof 'Javascript');
console.log(typeof new Array(3));
console.log(typeof new Date(2008, 10, 1));
解析
object
string
object

🔥 9. What’s the output?

console.log(1);

setTimeout(function () {
  console.log(2);
}, 0);

new Promise(function (resolve, reject) {
  console.log(3);
  resolve();
  console.log(4);
});

setTimeout(function () {
  console.log(5);
}, 0);
解析
1
3
4
2
5

10. What’s the output?

var arr = [1, 2, 3];
arr.shift();
arr.push(1);
arr.unshift(2);
console.log(arr.concat([1, 2]));
解析
[2, 2, 3, 1, 1, 2]

11. What’s the output?

var a = true + 1;
b = '3' + 0;
c = 5 + '12';
d = undefined + 11;
console.log(a + b + c + d);
解析
"230512NaN"

12. What’s the output?

for (var i = 0, j = 0; i < 10, j < 6; i++, j++) {
  var k = i + j;
}
console.log(k);
解析
10

13. What’s the output?

let obj1 = {
  birth: 1990,
  getAge: function () {
    var fn = function () {
      return this.birth;
    };
    return fn();
  },
};

let obj2 = {
  birth: 1990,
  getAge: function () {
    var fn = () => this.birth;
    return fn();
  },
};

console.log(obj1.getAge()); // undefined
console.log(obj2.getAge()); // 1990
解析
console.log(obj1.getAge()); // undefined
console.log(obj2.getAge()); // 1990

14. What’s the output?

console.log(a);
var a = 1;
console.log(b);
let b = 2;
console.log(b);
解析
undefined
ReferenceError: Cannot access 'b' before initialization
2

15. What’s the output?

(function foo() {
  var a = (b = 2);
})();

console.log(a); // Uncaught ReferenceError: a is not defined

console.log(b); // 2
解析
console.log(a); // Uncaught ReferenceError: a is not defined

console.log(b); // 2

🔥 16. What’s the output?

17. What’s the output?

let a = { n: 1 };
let b = a;
a.x = a = { n: 2 };

console.log(a.x); // 写出 a.x
console.log(b.x); // 写出 b.x
详解
console.log(a.x); // 写出 a.x => undefined
console.log(b.x); // 写出 b.x => { n: 2 }

18. What’s the output?

const x = 10;
const foo = {
  x: 90,
  getX: function () {
    return this.x;
  },
};
const getX = foo.getX;
console.log(foo.getX()); //   ?
console.log(getX()); //   ?
详解
console.log(foo.getX()); //  90
console.log(getX()); // undefined

19. What’s the output?

var num = 0;

function foo() {
  console.log('num1:' + num); // ?
}
function foo2() {
  console.log('num2:' + num); // ?
  var num = 1;
  foo();
}
foo2();
详解
var num = 0;

function foo() {
  console.log('num1' + num); // 本级作用域没有 num, 向上寻找全局作用域 => 0
}
function foo2() {
  var num; //  num 会提升声明,初始化为 undefined.
  console.log('num2' + num); //  本级作用域已有 num, 不会向上寻找 => undefined
  num = 1; ////  num 赋值为 1.
  console.log('num3' + num); // =>  1
  foo();
}
foo2();

JavaScript 只会提升声明,不会提升其初始化。如果一个变量先被使用再被声明和赋值的话,使用时的值是 undefined


20. What’s the output?

var func = (function (a) {
  console.log(a);
  this.a = a;
  return function (a) {
    a += this.a;
    return a;
  };
})(
  (function (a, b) {
    return a;
  })(1, 2),
);
func(4);
解析
5
var func = (function (a) {
  console.log(a); //1
  this.a = a;
  return function (a) {
    console.log(a); //4
    a += this.a;
    console.log(a); //5
    return a;
  };
})(
  (function (a, b) {
    console.log(a, b); //1,2
    return a;
  })(1, 2),
);
func(4);

21. What’s the output? CSS 选择器优先级

Specificity

解析

下面列表中,选择器类型的优先级是递增的:

  1. 类型选择器(例如,h1) 和伪元素 (例如,::before)
  2. 类选择器 (例如,.example), 属性选择器 (例如,[type="radio"]) 和伪类 (例如,:hover)
  3. ID 选择器(例如,#example).

通配选择符 (universal selector)(*) 关系选择符 (combinators)(+, >, ~, ‘ ‘, ||) 和 否定伪类 (negation pseudo-class)(:not()) 对优先级没有影响。(但是,:not() 内部声明的选择器会影响优先级).

您可以访问  “Specificity” in “Cascade and inheritance”  或者  https://specifishity.com  来了解更多关于优先级的详细信息。

给元素添加的 内联样式 (例如,style="font-weight:bold") 总会覆盖外部样式表的任何样式 , 因此可看作是具有最高的优先级。

CSS 选择器优先级从高到低的排序如下:

选择器 用法
id 选择器 #myid
类选择器 .myclassname
标签选择器 div,h1,p
相邻选择器 h1+p
子选择器 ul > li
后代选择器 li a
通配符选择器 *
属性选择器 a[rel="external"]
伪类选择器 a:hover, li:nth-child

22. What’s the output?

console.log({}.valueOf());
console.log([].valueOf());

console.log({}.toString());
console.log([].toString());
解析

{}Object.prototype.valueOf() 结果为 {} ,Object.prototype.toString() 的结果为 "[object Object]"

[] 的 valueOf 结果为 [] ,Array.prototype.toString() 的结果为 ""

console.log({}.valueOf()); // {  }
console.log([].valueOf()); // []

console.log({}.toString()); // "[object Object]"
console.log([].toString()); // ""

23. What’s the output?

const [x = 1] = [null];
const [y = 1] = [undefined];
console.log(`x:${x}, y:${y}`); //  输出结果?

const { a } = { a: null };
const { b } = { b: undefined };
console.log(`a:${a}, b:${b}`); //  输出结果?
解析
const [x = 1] = [null];
const [y = 1] = [undefined];

console.log(`x:${x}, y:${y}`); //  x:null, y:1

const { a } = { a: null };
const { b } = { b: undefined };
console.log(`a:${a}, b:${b}`); //  a:null, b:undefined

解构赋值


24. What’s the output?

const a = { x: 1 };
a.x = 2;
console.log(a); // 输出结果?

const b = { x: 1 };
b['x'] = 3; // 输出结果?
console.log(b);

const c = { x: 1 };
c = { x: 4 }; // 输出结果?
console.log(c);
解析
const a = { x: 1 };
a.x = 2;
console.log(a); // { x: 2 }

const b = { x: 1 };
b['x'] = 3; // { x: 3 }
console.log(b);

const c = { x: 1 };
c = { x: 4 };
console.log(c); //  TypeError: Assignment to constant variable.

const 常量是块级范围的,非常类似用 let 语句定义的变量。但常量的值是无法(通过重新赋值)改变的,也不能被重新声明。

属性访问器 提供了两种方式用于访问一个对象的属性,它们分别是点号和方括号。


25. What’s the output?

let x = 1;
let y = x++;
let z = x++ + ++y;

console.log(`x:${x}, y:${y}, z:${z}`); //  输出结果?

Increment (++)

解析
let x = 3;
const y = x++;

console.log(`x:${x}, y:${y}`);
// expected output: "x:4, y:3"

let a = 3;
const b = ++a;

console.log(`a:${a}, b:${b}`);
// expected output: "a:4, b:4"
let x = 1;
let y = x++;
let z = x++ + ++y;

console.log(`x:${x}, y:${y}, z:${z}`);
// expected output: "x:3, y:2, z:4"

26. What’s the output?

let k = 0;

for (let i = 0; i < 10; i++) {
  for (let j = 0; j < 10; j++) {
    if (i == 5 && j == 5) {
      k++;
      continue;
    }
  }
}
console.log(k); // 输出结果?
let k = 0;

for (let i = 0; i < 10; i++) {
  for (let j = 0; j < 10; j++) {
    if (i == 5 && j == 5) {
      continue;
    }
    k++;
  }
}
console.log(k); // 输出结果?

continue 声明终止当前循环或标记循环的当前迭代中的语句执行,并在下一次迭代时继续执行循环。

解析
let k = 0;

for (let i = 0; i < 10; i++) {
  for (let j = 0; j < 10; j++) {
    if (i == 5 && j == 5) {
      k++;
      continue;
    }
  }
}
console.log(k); //  输出结果 1
let k = 0;

for (let i = 0; i < 10; i++) {
  for (let j = 0; j < 10; j++) {
    if (i == 5 && j == 5) {
      continue;
    }
    k++;
  }
}
console.log(k); // 输出结果 99

27. What’s the output?

const numbers = [1, 30, 4, 21, 100000];
numbers.sort();
console.log(numbers); // 输出结果 ?
numbers.sort((a, b) => a - b);
console.log(numbers); // 输出结果 ?

Array.prototype.sort() - JavaScript | MDN

解析
const numbers = [1, 30, 4, 21, 100000];
numbers.sort();
console.log(numbers); // 输出结果:[1, 100000, 21, 30, 4]

numbers.sort((a, b) => a - b);
console.log(numbers); // 输出结果:[ 1, 4, 21, 30, 100000 ]

28. What’s the output?

[1, 2, 3, 4, 5].shift(); // 输出结果?
[1, 2, 3, 4, 5].push(''); // 输出结果?
[1, 2, 3, 4, 5].unshift(''); // 输出结果?
[1, 2, 3, 4, 5].pop(); // 输出结果?
解析
[1, 2, 3, 4, 5].shift(); // 输出结果 1
[1, 2, 3, 4, 5].push(''); // 输出结果 6
[1, 2, 3, 4, 5].unshift(''); // 输出结果 6
[1, 2, 3, 4, 5].pop(); // 输出结果 5

Array.prototype.shift() - JavaScript | MDN 方法从数组中 ** 删除第一个元素 **, 并 ** 返回该元素的值 **. 此方法更改数组的长度(该方法修改原有数组).

const array1 = [1, 2, 3];
const firstElement = array1.shift();
console.log(array1);
// expected output: Array [2, 3]
console.log(firstElement);
// expected output: 1

Array.prototype.push() 方法将一个或多个元素 ** 添加到数组的末尾 **, 并 ** 返回该数组的新长度 ** (该方法修改原有数组).

const animals = ['pigs', 'goats', 'sheep'];
const count = animals.push('cows');
console.log(count);
// expected output: 4
console.log(animals);
// expected output: Array ["pigs", "goats", "sheep", "cows"]

Array.prototype.unshift() 方法将一个或多个元素 ** 添加到数组的开头 **, 并 ** 返回该数组的新长度 ** (该方法修改原有数组).

const array1 = [1, 2, 3];
console.log(array1.unshift(4, 5));
// expected output: 5
console.log(array1);
// expected output: Array [4, 5, 1, 2, 3]

Array.prototype.pop() 方法从数组中 ** 删除最后一个元素 **, 并 ** 返回该元素的值 **. 此方法更改数组的长度 (该方法修改原有数组).

const plants = ['broccoli', 'cauliflower', 'cabbage', 'kale', 'tomato'];
console.log(plants.pop());
// expected output: "tomato"
console.log(plants);
// expected output: Array ["broccoli", "cauliflower", "cabbage", "kale"]

29. What’s the output?

parseInt(10 % 5); // 输出结果?
parseInt('421hop'); // 输出结果?

parseInt() - JavaScript | MDN

Remainder (%)

解析
parseInt(10 % 5); // 输出结果 0
parseInt('421hop'); // 输出结果 421

30. What’s the output?

2 << 2; // 输出结果?
9 << 3; // 输出结果?

Left shift (<<)

解析
2 << 2; // 输出结果 8
// 2 * (2 ** 2) = 2 * (4) = 8

9 << 3; // 输出结果 72
// 9 * (2 ** 3) = 9 * (8) = 72

31. What’s the output? HTML5 新增的表单元素?

解析
Type 描述
color 用于指定颜色的控件;在支持的浏览器中,激活时会打开取色器。
date 输入日期的控件(年、月、日,不包括时间)。在支持的浏览器激活时打开日期选择器或年月日的数字滚轮。
month 输入年和月的控件,没有时区。
range 此控件用于输入不需要精确的数字。控件是一个范围组件,默认值为正中间的值。
search 用于搜索字符串的单行文字区域。
tel 用于输入电话号码的控件。拥有动态键盘的设备上会显示电话数字键盘。
time 用于输入时间的控件,不包括时区。
url 用于输入 URL 的控件。类似 text 输入,但有验证参数,在支持动态键盘的设备上有相应的键盘。

<input>: The Input (Form Input) element


32. What’s the output?

null == undefined; // 输出结果?
0 == null; // 输出结果?

A model for understanding equality comparisons?

解析
null == undefined; // 输出结果 true
0 == null; // 输出结果 false
null undefined
We declare a variable and assign it a value of null. We declare a variable without assigning any value.
type of (null) “object” type of (undefined) “undefined”

33. What’s the output?

setTimeout(function () {
  console.log(4);
}, 0);

new Promise(function (resolve) {
  console.log(1);
  resolve();
  console.log(2);
}).then(function () {
  console.log(5);
});

console.log(3);
解析
1
2
3
5
4

🔥 34. What’s the output?

async/await、promise 和 setTimeout 的执行顺序 实战 🔥

async function async1() {
  console.log('async1 start'); // 2
  await async2();
  console.log('asnyc1 end'); // 6
}
async function async2() {
  console.log('async2'); // 3
}
console.log('script start'); // 1
setTimeout(() => {
  console.log('setTimeOut'); // 8
}, 0);
async1();
new Promise(function (reslove) {
  console.log('promise1'); // 4
  reslove();
}).then(function () {
  console.log('promise2'); // 7
});
console.log('script end'); // 5
详解

async/await、promise 和 setTimeout 的执行顺序 实战

  • 执行 console.log('script start'),输出 script start
  • 执行 setTimeout,是一个异步动作,放入宏任务异步队列中;
  • 执行 async1(),输出 async1 start,继续向下执行;
  • 执行 async2(),输出 async2,并返回了一个 promise 对象,await 让出了线程,把返回的 promise 加入了微任务异步队列,所以 async1() 下面的代码也要等待上面完成后继续执行;
  • 执行 new Promise,输出 promise1,然后将 resolve 放入微任务异步队列;
  • 执行 console.log('script end'),输出 script end
  • 到此同步的代码就都执行完成了,然后去微任务异步队列里去获取任务
  • 接下来执行 resolve(async2 返回的 promise 返回的),输出了 async1 end
  • 然后执行 resolve(new Promise 的),输出了 promise2
  • 最后执行 setTimeout,输出了 setTimeOut
script start
async1 start
async2
promise1
promise3
script end
asnyc1 end
promise2
setTimeOut

⭕️ 35. What’s the output?

function Fn() {
  this.x = 100;
  this.y = 200;
  this.getX = function () {
    console.log(this.x);
  };
}
Fn.prototype.getX = function () {
  console.log(this.x);
};
Fn.prototype.getY = function () {
  console.log(this.y);
};
var f1 = new Fn();
var f2 = new Fn();
console.log(f1.getX === f2.getX); //false
console.log(f1.getY === f2.getY); //true
console.log(f1.__proto__.getY === Fn.prototype.getY); //true
console.log(f1.__proto__.getX === f2.getX); //false
console.log(f1.getX === Fn.prototype.getX); //false
console.log(f1.constructor); //Fn
console.log(Fn.prototype.__proto__.constructor); // Object
f1.getX(); //this:f1 => console.log(f1.x) =>100
f1.__proto__.getX(); //this:f1.__proto__=>console.log(f1.__proto__.x)=>undefined
f2.getY(); //this:f2=>console.log(f2.y)=>200
Fn.prototype.getY(); //this:Fn.prototype=>console.log(Fn.prototype.y)=>undefined
详解

⭕️ 36. What’s the output?

详解

⭕️ 37. What’s the output?

详解

38. What’s the output?

3 + '2' - 5;
详解
27

39. What’s the output?

0.1 + 0.7;
详解
0.7999999999999999
+(0.1 + 0.7).toLocaleString(); // 0.8

40. What’s the output?

0.1 + 0.2;
详解
0.30000000000000004
+(0.1 + 0.2).toLocaleString(); // 0.3

41. What’s the output?

const array = [];

for (var i = 0; i < 3; i++) {
  array.push(() => i);
}
const result = array.map((item) => item());

console.log(result); // => [3,3,3]
详解

42. What’s the output?

const array = [];

for (let i = 0; i < 3; i++) {
  array.push(() =i);
}
const result = array.map((item) =item());

console.log(result); // => [1,2,3]
详解

单选框组件

<div class='form_title'> 项目需求(单选)<span>*</span></div>
<div class="group">
  <label>
    <input type="radio" name="parent_radio" value="积分商城搭建" />
    积分商城搭建 & lt;/label>
  <label>
    <input type="radio" name="parent_radio" value="积分礼品供应" />
    积分礼品供应 & lt;/label>
  <label>
    <input type="radio" name="parent_radio" value="积分商城代运营" />
    积分商城代运营 & lt;/label>
  <label>
    <input type="radio" name="parent_radio" value="其他定制需求" />
    其他定制需求 & lt;/label>
</div>
<p></p>
input[type="radio"] {
  /* 取消自带按钮 */
  /*display: none;*/
}
.group > label {
  /* 未选中状态 */
  float: left;
  color: #333333;
  padding: 7px 14px;
  border: 1px solid #333333;
  margin: 0 15px 15px 0;
  width: 150px;
}
.group > label.active {
  /* 选中状态 */
  color: #1770e6;
  padding: 7px 14px;
  border: 1px solid #1770e6;
}
.group {
  width: 400px;
  height: 300px;
  margin: 20px auto;
}
.form_title {
  width: 150px;
  color: #333;
  margin: 20px auto;
}
.form_title span {
  color: #ff2e3e;
}
body {
  text-align: center;
}
[...document.querySelectorAll(".group label")].forEach((item) => {
  item.onclick = function () {
    [...document.querySelectorAll(".group label")].forEach((i) =>
      i.classList.remove("active")
    );
    item.classList.toggle("active");
    // event.currentTarget.classList.toggle("active");
  };
});
[...document.querySelectorAll(".group input")].forEach((item) => {
  item.onclick = function () {
    console.log(document.querySelector('input[type="radio"]:checked').value);
    document.querySelector("p").textContent = document.querySelector('input[type="radio"]:checked').value;
  };
});

多选框组件

<div class='form_title'> 项目需求(多选)<span>*</span></div>
<div class="group">
  <label>
    <input type="checkbox" name="parent_checkbox" value="积分商城搭建" id="" />
    积分商城搭建 & lt;/label>
  <label>
    <input type="checkbox" name="parent_checkbox" value="积分礼品供应" id="" />
    积分礼品供应 & lt;/label>
  <label>
    <input type="checkbox" name="parent_checkbox" value="其他定制需求" id="" />
    其他定制需求 & lt;/label>
  <label>
    <input type="checkbox" name="parent_checkbox" value="积分商城代运营" id="" />
    积分商城代运营 & lt;/label>
</div>
<p></p>
input[type="checkbox"] {
  /* 取消自带按钮 */
  display: none;
}
.group > label {
  /* 未选中状态 */
  float: left;
  color: #333333;
  padding: 7px 14px;
  border: 1px solid #333333;
  margin: 0 15px 15px 0;
  width: 150px;
}
.group > label.active {
  /* 选中状态 */
  color: #1770e6;
  padding: 7px 14px;
  border: 1px solid #1770e6;
}
.group {
  width: 400px;
  height: 300px;
  margin: 20px auto;
}
.form_title {
  width: 150px;
  color: #333;
  margin: 20px auto;
}
.form_title span {
  color: #ff2e3e;
}
body {
  text-align: center;
}
[...document.querySelectorAll(".group label")].forEach((item, index) => {
  item.onclick = function (event) {
    if (event.target.matches("input")) return;
    const i = document.querySelector(
      ".group label:nth-child(" + (index + 1) + ")"
    ).classList;
    i.contains("active") ? i.remove("active") : i.add("active");
  };
});
[...document.querySelectorAll(".group input")].forEach((item) => {
  item.onclick = () => {
    const arr = [
      ...document.querySelectorAll('input[type="checkbox"]:checked')
    ].map((i) => i.value);
    console.log(arr);
    document.querySelector("p").textContent = arr;
  };
});

5 个 HTML5 新增的标签和 5 个 CSS3 新增属性

解析

5 个 HTML5 新增的标签:<article>,<nav>, <audio> , <video> ,<main>,<header>, <footer>

HTML5 标签列表

5 个 CSS3 新增属性: word-wrap 文字换行 text-overflow 超过指定容器的边界时如何显示 text-decoration 文字渲染 text-shadow 文字阴影 gradient 渐变效果 transition 过渡效果 transition-duration 过渡的持续时间 transform 拉伸,压缩,旋转,偏移等变换 animation 动画

transition 需要触发一个事件才能改变属性,而 animation 不需要触发任何事件的情况下才会随时间改变属性值,并且 transition 为 2 帧,从 from … to,而 animation 可以一帧一帧的。

CSS3

iframe 内外

js 在 iframe 子页面操作父页面元素 js 在父页面获取 iframe 子页面元素

table

  • 一个可选的 <caption> 元素
  • 零个或多个的 <colgroup> 元素
  • 零个或多个的 <col> 元素
  • 一个可选的 <thead> 元素
  • 下列任意一个:
    • 零个或多个 <tbody>
    • 零个或多个 <tr>
  • 一个可选的 <tfoot> 元素

HTML5 存储类型有什么区别?

共同点:都是在客户端存储数据,且是同源的。

区别:

  • 存储大小不一样;cookie 存储数据最大只能为 4kb,而 sessionStorage 与 localStorage 可以保存 5MB 甚至更多数据。Cookie 数据始终在同源的 http 请求中携带,即 cookie 在浏览器与服务器之间来回传递,而 sessionStorage 与 localStorage 不会自动发给服务端,仅在本地保存。

  • 数据有效期不同;sessionStorage 仅在当前浏览器窗口未关闭之前有效(同源的新窗口不生效),localStorage 仅在当前的浏览器下永久生效(不同的浏览器不能共享数据),不管关闭了 重新打开的 还是生效的。Cookie 只在设置的 cookie 过期时间之前一直有效,即使窗口或者浏览器关闭,或者打开新的同源窗口。

  • 作用域不同;sessionStorage 不在不同的浏览器窗口中共享,即是同一个页面,localStorage 在所有的同源窗口(子域之间不能共享数据,不支持改 domain,可以用 postMassage)中都是共享的(只在相同的浏览器下),cookie 在所有的同源窗口(子域之间可以把各自的 domain 改成和主域一样来实现共享数据)都是共享的(仅在同一个浏览器中)。

浏览器缓存 应用缓存

反转字符串

const reverseString = (str) => str.split('').reverse().join('');

console.log(reverseString('h,e,l,l,o'));

JavaScript 字符串反转的三种方式

CSS 属性名 选择器 是否区分大小写 (5 分)

解析
  • id 名和 class 名是区分大小写的
  • 选择器中,只有标签选择器不区分大小写
  • 属性名和属性值是不区分大小写的

Cookie 有效期

解析

The lifetime of a cookie can be defined in two ways:

  • Session cookies are deleted when the current session ends. The browser defines when the “current session” ends, and some browsers use session restoring when restarting, which can cause session cookies to last indefinitely long.
  • Permanent cookies are deleted at a date specified by the Expires attribute, or after a period of time specified by the Max-Age attribute.

SameSite attribute


clienwidth offsetwidth border-box content-box (5 分)

<div id="content-box">
  By default in the CSS box model, the width and height you assign to an element is applied only to
  the element's content box. If the element has any border or padding, this is then added to the
  width and height to arrive at the size of the box that's rendered on the screen. This means that
  when you set width and height,
</div>
<div id="border-box">
  you have to adjust the value you give to allow for any border or padding that may be added. For
  example, if you have four boxes with width: 25%;, if any has left or right padding or a left or
  right border, they will not by default fit on one line within the constraints of the parent
  container.
</div>
div {
  width: 460px;
  height: 180px;
  padding: 20px;
  border: 8px solid red;
  background: yellow;
  margin: 10px;
}
#content-box {
  box-sizing: content-box;
  /* Total width: 460px + (2 * 20px) + (2 * 8px) = 516px
   Total height: 180px + (2 * 20px) + (2 * 8px) = 236px
   Content box width: 460px
   Content box height: 180px */
}
#border-box {
  box-sizing: border-box;
  /* Total width: 460px
   Total height: 180px
   Content box width: 460px - (2 * 20px) - (2 * 8px) = 404px
   Content box height: 180px - (2 * 20px) - (2 * 8px) = 124px */
}
const content = document.querySelector('#content-box');
const border = document.querySelector('#border-box');

console.log(content.offsetWidth); // 输出结果?
console.log(content.clientWidth); // 输出结果?

console.log(border.offsetWidth); // 输出结果?
console.log(border.clientWidth); // 输出结果?
解析
<div id="content-box"> By default in the CSS box model, the width and height you assign to an element is applied only to the element's content box. If the element has any border or padding, this is then added to the width and height to arrive at the size of the box that's rendered on the screen. This means that when you set width and height,</div>
<div id="border-box">you have to adjust the value you give to allow for any border or padding that may be added. For example, if you have four boxes with width: 25%;, if any has left or right padding or a left or right border, they will not by default fit on one line within the constraints of the parent container.
</div>
div {
  width: 460px;
  height: 180px;
  padding: 20px;
  border: 8px solid red;
  background: yellow;
  margin: 10px;
}
#content-box {
  box-sizing: content-box;
  /* Total width: 460px + (2 * 20px) + (2 * 8px) = 516px
   Total height: 180px + (2 * 20px) + (2 * 8px) = 236px
   Content box width: 460px
   Content box height: 180px */
}
#border-box {
  box-sizing: border-box;
  /* Total width: 460px
   Total height: 180px
   Content box width: 460px - (2 * 20px) - (2 * 8px) = 404px
   Content box height: 180px - (2 * 20px) - (2 * 8px) = 124px */
}
const content = document.querySelector("#content-box");
const border = document.querySelector("#border-box");
console.log(content.offsetWidth); //517(应该是 516 517 是 codepen 的问题)
console.log(content.clientWidth); //500
console.log(border.offsetWidth); //460
console.log(border.clientWidth); //444
const content = document.querySelector('#content-box');
const border = document.querySelector('#border-box');

console.log(content.offsetWidth); //516
// 460px + (2 * 20px) + (2 * 8px) = 516px
console.log(content.clientWidth); //500
// 516px - (2 * 8px) = 500px
console.log(border.offsetWidth); //460
// 460px
console.log(border.clientWidth); //444
// 460px - (2 * 8px) = 444px

HTMLElement.offsetWidth 是测量包含元素的边框 (border)、水平线上的内边距 (padding)、竖直方向滚动条 (scrollbar)(如果存在的话)、以及 CSS 设置的宽度 (width) 的值。(content + padding + border)

Element.clientWidth 内联元素以及没有 CSS 样式的元素的 clientWidth 属性值为 0.Element.clientWidth 属性表示元素的内部宽度,以像素计。该属性包括内边距 padding, 但 ** 不包括 ** 边框 border、外边距 margin 和垂直滚动条(如果有的话). (content + padding)

box-sizing 属性定义了 应该如何计算一个元素的总宽度和总高度

content-box gives you the default CSS box-sizing behavior. If you set an element’s width to 100 pixels, then the element’s content box will be 100 pixels wide, and the width of any border or padding will be added to the final rendered width, making the element wider than 100px.

border-box tells the browser to account for any border and padding in the values you specify for an element’s width and height. If you set an element’s width to 100 pixels, that 100 pixels will include any border or padding you added, and the content box will shrink to absorb that extra width. This typically makes it much easier to size elements.


清除浮动

clear

CSS Clear: Learn to Clear Floating Elements

解析

clear CSS 属性指定一个元素是否必须移动 (清除浮动后) 到在它之前的浮动元素下面。clear 属性适用于浮动和非浮动元素。

  • 父元素结束标签之前插入清除浮动的块级元素
  • 利用 after 伪元素 (clearfix)
  • 利用 overflow 清除浮动

数组去重

const arr = [1, '', true, undefined, null, NaN, { a: 1 }, { a: 2 }, {}, Symbol('a')];
解析
  • 利用 Set 去重

无法去重对象 Symbol

const unique = (arr) => [...new Set(arr)];

console.log(unique(arr));
// [1, '', true, undefined, null, NaN, { a: 1}, { a: 2 }, {}, {}, Symbol(a), Symbol(a)];
  • 利用 hasOwnProperty 判断是否存在对象属性

利用一个空的 Object 对象,我们把数组的值存成 Object 的 key 值,比如 Object[value1] = true, 在判断另一个值的时候,如果 Object[value2] 存在的话,就说明该值是重复的

因为 1'1' 是不同的,但是这种方法会判断为同一个值,这是因为对象的键值只能是字符串,所以我们可以使用 typeof item + item 拼成字符串作为 key 值来避免这个问题

无法正确区分出两个对象,比如 {a: 1}{a: 2}, 因为 typeof item + item 的结果都会是 object[object Object], 不过我们可以使用 JSON.stringify 将对象序列化

Object.prototype.hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键).

const unique = (arr) => {
  var obj = {};
  return arr.filter((item) => {
    return obj.hasOwnProperty(typeof item + JSON.stringify(item))
      ? false
      : (obj[typeof item + JSON.stringify(item)] = true);
  });
};

console.log(unique(arr));
// [1, '', true, undefined, null, NaN, { a: 1}, { a: 2 }, {}, Symbol(a)];
// 所有的都去重了

以下属于脱离文档流布局的样式 (2 分)

A、float:left
B、position: relative
C、margin-left: -50%
D、transform: translate(50%)

详解

A

所谓的文档流,指的是元素排版布局过程中,元素会自动从左往右,从上往下的流式排列。并最终窗体自上而下分成一行行,并在每行中按从左至右的顺序排放元素。脱离文档流即是元素打乱了这个排列,或是从排版中拿走。当前所知的脱离文档流的方式有两种:浮动和定位。

脱离常规流的元素

  • floated items.
  • items with position: absolute (including position: fixed which acts in the same way).
  • the root element (html)

position

float


以下属 Javascript 原始数据类型的是 (2 分)

A、 undefined
B、 array
C、 symbol
D、 object

详解

AC

在 JavaScript 中,共有 7 种 基本类型: string, number, bigint, boolean, undefined, symbol, and null.


以下结果正确的是 (3 分)

A、 console.log(parseInt('ui')); //NaN
B、 console.log(0.1 + 0.2); //0.3
C、 console.log(19.9 * 100); //1990
D、 console.log(19.9 * 10); //199

详解

AD

console.log(parseInt('ui')); //NaN

console.log(0.1 + 0.2); //0.30000000000000004

console.log(19.9 * 100); //1989.9999999999998

console.log(19.9 * 10); //199

display: nonevisibility: hidden 的区别 (5 分)

详解

在使用 CSS 隐藏一些元素时,我们经常用到 display: nonevisibility: hidden. 两者差别如下:

  • 是否占据空间:display: none, 该元素不占据任何空间,在文档渲染时,该元素如同不存在 (但依然存在文档对象模型树中). visibility: hidden, 该元素空间依旧存在。即 (display: none) 不会在渲染树中出现,(visibility: hidden) 会。

  • 是否渲染:display: none, 会触发 reflow(回流), 进行渲染。visibility: hidden, 只会触发 repaint(重绘), 因为没有发现位置变化,不进行渲染。

  • 是否是继承属性:display: none,display 不是继承属性,元素及其子元素都会消失。visibility: hidden,visibility 是继承属性,若子元素使用了 visibility: hidden, 则不继承,这个子孙元素又会显现出来。

  • 读屏器是否读取:读屏器不会读取 display: none 的元素内容,而会读取 visibility: hidden 的元素内容。


TCP/IP 协议族按层次分为哪 4 层?HTTP 中的三次握手属于哪一层 (10 分)

详解


Search Insert Position (15 分)

给定一个排序数组 (假设数组中无重复元素) 和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置

searchInsert([1, 3, 5, 6], 5); // 输出 2
searchInsert([1, 3, 5, 6], 2); // 输出 1
searchInsert([1, 3, 5, 6], 7); // 输出 4
searchInsert([1, 3, 5, 6], 0); // 输出 0
searchInsert([1], 0); // 输出 0
详解
  • 解决方案:二分查找

Right shift (>>)

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
const searchInsert = (nums, target) => {
  const n = nums.length;
  let left = 0,
    right = n - 1,
    ans = n;
  // 这里需要注意,循环条件
  while (left <= right) {
    // 这里需要注意,计算 mid (> 和>> 都可以)
    let mid = ((right - left) >> 1) + left;
    if (target <= nums[mid]) {
      ans = mid;
      // 这里需要注意,移动右指针
      right = mid - 1;
    } else {
      // 这里需要注意,移动左指针
      left = mid + 1;
    }
  }
  return ans;
};
  • 解决方案一
/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
const searchInsert = (nums, target) => {
  let r;
  // 判断在数组中是否找到目标
  if (nums.includes(target)) {
    // 在数组中找到目标,返回其索引
    r = nums.indexOf(target);
  } else {
    // 在数组中没有找到目标,遍历数组
    for (let i = 0; i < nums.length; i++) {
      // 如果数组中找到大于目标值
      if (nums[i] > target) {
        // 返回数组其索引
        r = i;
        // 中止当前循环
        break;
      } else {
        // 返回数组的长度
        r = nums.length;
      }
    }
  }
  return r;
};
  • 优化方案二
/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
const searchInsert = (nums, target) => {
  let r;
  // 判断在数组中是否找到目标
  if (nums.includes(target)) {
    // 在数组中找到目标,返回其索引
    r = nums.indexOf(target);
  } else {
    // 遍历数组 nums 判断是否有数组元素 element 大于 target
    const isLargeNumber = (element) => element > target;
    // 如果有,则返回该 element 在数组 nums 的索引
    // 否则返回 数组 nums 的元素个数
    r = nums.findIndex(isLargeNumber) !== -1 ? nums.findIndex(isLargeNumber) : nums.length;
  }
  return r;
};
  • 精简方案二
/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
const searchInsert = (nums, target) => {
  const isLargeNumber = (element) => element > target;
  return nums.includes(target)
    ? nums.indexOf(target)
    : nums.findIndex(isLargeNumber) !== -1
    ? nums.findIndex(isLargeNumber)
    : nums.length;
};
  • 测试
console.log(searchInsert([1, 3, 5, 6], 5)); // 输出 2

console.log(searchInsert([1, 3, 5, 6], 2)); // 输出 1

console.log(searchInsert([1, 3, 5, 6], 7)); // 输出 4

console.log(searchInsert([1, 3, 5, 6], 0)); // 输出 0

console.log(searchInsert([1], 0)); // 输出 0

px em rem

解析

px 像素 1px = 1/96th of 1in

em 在 font-size 中使用是相对于父元素的字体大小,在其他属性中使用是相对于自身的字体大小,如 width

rem 根元素的字体大小


HTTP HTTPS

HTTP 协议传输的数据都是未加密的,也就是明文的,因此使用 HTTP 协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,于是网景公司设计了 SSL(Secure Sockets Layer)协议用于对 HTTP 协议传输的数据进行加密,从而就诞生了 HTTPS. 简单来说,HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,要比 HTTP 协议安全。

HTTPS 和 HTTP 的区别主要如下:

  • HTTP 明文传输,数据都是未加密的,安全性较差,HTTPS(SSL+HTTP) 数据传输过程是加密的,安全性较好。
  • 使用 HTTPS 协议需要到 CA(Certificate Authority,数字证书认证机构) 申请证书,一般免费证书较少,因而需要一定费用。
  • HTTP 页面响应速度比 HTTPS 快,主要是因为 HTTP 使用 TCP 三次握手建立连接,客户端和服务器需要交换 3 个包,而 HTTPS 除了 TCP 的三个包,还要加上 SSL 握手需要的 9 个包,所以一共是 12 个包。
  • HTTP 和 HTTPS 使用的是完全不同的连接方式,用的端口也不一样,前者是 80,后者是 443。
  • HTTPS 其实就是建构在 SSL/TLS 之上的 HTTP 协议,所以,要比较 HTTPS 比 HTTP 要更耗费服务器资源。

内存回收

内存管理


根据指定的字段和规则排序对象数组

var data = [
  { area: '深圳', percentage: 15, number: 80, staytime: 2 },
  { area: '北京', percentage: 30, number: 150, staytime: 4 },
  { area: '广州', percentage: 25, number: 60, staytime: 3 },
  { area: '上海', percentage: 30, number: 100, staytime: 4 },
];
解析
/*
 * 根据指定的字段和规则排序数据
 * data Array 要排序的数据
 * field string 排序依据的字段
 * rule string 排序规则 DESC / ASC
 * throw
 *       data is invalid : 要排序的数据不存在或类型不正确
 *       field is invalid : 排序参考字段不存在
 * return Array 排序后的数据
 */
const mySort = (data, field, rule = 'ASC') => {
  if (!(data instanceof Array)) {
    throw new TypeError('data is invalid');
  }
  if (!(field in data[0])) {
    throw new RangeError('field is invalid');
  }

  data.sort(function (a, b) {
    let v = a[field] - b[field];
    return rule == 'ASC' ? v : -v;
  });
};
mySort(data, 'number', 'desc');
console.dir(data);

Array.prototype.sort() - JavaScript | MDN :  The sort() method sorts the elements of an array in place and returns the array.

Array.prototype.indexOf() - JavaScript | MDN :  The indexOf() method returns the first index at which a given element can be found in the array, or -1 if it is not present.


块级元素和行内元素都有哪些 ?

解析

Inline elements : An inline element does not start on a new line and only takes up as much width as necessary.<a>,<code>,<img>,<span>,<button>,<input>,<label>,<select>

Block-level elements : A block-level element always starts on a new line and takes up the full width available (stretches out to the left and right as far as it can).<article><aside><canvas><footer><header><section> <video><audio>



data-* 属性的作用是什么 ?

解析

data-


三列式布局,左右各 200px, 中间自适应宽度 ?

解析

传说中的 “双飞翼布局?圣杯布局?尿壶布局?”, 还有中间要先加载没提么?

.left {
  position: absolute;
  left: 0;
  top: 0;
  width: 200px;
}
.right {
  position: absolute;
  right: 0;
  top: 0;
  width: 200px;
}
.center {
  margin: 0 200px;
}
<div class="left"></div>
<div class="center"></div>
<div class="right"></div>

请用 JavaScript 实现以下逻辑:

现有一数组 a, 长度未知

  1. 如果 a 的长度为零,则为其添加一个新项,值为 1.
  2. 如果 a 的长度不为零,则按照 先进先出 的原则,移除一项。
  3. 评分标准:代码越精简越好,<=30 字符 10 分,<=35 字符 6 分,>35 字符 3 分 (空格不算)
解析

参考解析

a[a.length ? 'shift' : 'push'](1); //30
a.length ? a.shift() : a.push(1); //29 (最佳方案)
// 纯数组方法的使用,只要知道数组这几个方法的用法就能看明白。

a.length 不为零,执行 a.shift() 移除数组中第一个元素为并返回该元素 否则执行 a.push(1) 将元素  1  添加到数组的末尾,并返回数组的新长度。


用 JavaScript 实现以下功能:

不使用 loop 生成一个长度 n=100, 每一项的数据类型为 Number, 值等于其下标的数组 array.

解析
// ES6 (推荐,最佳方案)
Array.from(Array(100).keys());

列举 3 种提取网页所有链接的方法,并说明其优劣

解析

方法一:通过 DOM 分析

var aElements = document.querySelectorAll('a');
for (var i = 0; i < aElements.length; i++) {
  if (aElements[i].href) {
    console.log(aElements[i].href);
  }
}

方法二:通过正则匹配

var docHtml = document.documentElement.innerHTML;
var data = [];
docHtml.replace(/<a[^>]*href=(['"])([^>'"]+)\1[^>]*>/gi, function ($0, $1, $2) {
  data.push($2);
});
console.dir(data);

补全

let a = { name: 'Jack', age: 18 };
let /* ______ */ = a;
console.log(name); //Jack
解析
let a = { name: 'Jack', age: 18 };
let { name, age } = a;
console.log(name); //Jack

如何查找一篇英文文章中出现频率最高的单词?

解析
function findMostWord(article) {
  // 合法性判断
  if (!article) return;

  // 参数处理
  article = article.trim().toLowerCase();

  let wordList = article.match(/[a-z]+/g),
    visited = [],
    maxNum = 0,
    maxWord = '';

  article = '' + wordList.join('  ') + ' ';

  // 遍历判断单词出现次数
  wordList.forEach(function (item) {
    if (visited.indexOf(item) < 0) {
      // 加入 visited
      visited.push(item);

      let word = new RegExp('' + item + ' ', 'g'),
        num = article.match(word).length;

      if (num > maxNum) {
        maxNum = num;
        maxWord = item;
      }
    }
  });

  return maxWord + ' ' + maxNum;
}

innerHTML outerHTML innerText

解析

对于这样一个 HTML 元素:<div>content<br/></div>.

innerHTML: 内部 HTML,content<br/>;
outerHTML: 外部 HTML,<div>content<br/></div>;
innerText: 内部文本,content ;
outerText: 内部文本,content ;

外边距合并

解析

label

解析

问题

A、B 两人分别在两座岛上,B 生病了,A 有 B 所需要的药。
C 有一首小船和一个可以上锁的箱子。C 愿意在 A 和 B 之间运东西,但是东西只能放在箱子里。只要箱子没被上锁,C 都会偷走箱子里的东西,不管箱子里有什么。
如果 A 和 B 各有一把锁和只能开自己那把锁的钥匙,A 应该如何把药品安全地递交给 B?

解析

A 把药放进箱子,用自己的锁把箱子锁上。B 拿到箱子后,再在箱子上加一把自己的锁。箱子运回 A 后,A 取下自己的锁。箱子再运到 B 手中时,B 取下自己的锁,获得药物。


二叉树的节点的对称序列 后序序列 前序序列是

解析

遍历二叉树:L、D、R 分别表示遍历左子树、访问根结点和遍历右子树,则先(根)序遍历二叉树的顺序是 DLR,中(根)序遍历二叉树的顺序是 LDR,后(根)序遍历二叉树的顺序是 LRD

  • 后序遍历最后一个结点一定是根结点
  • 先序遍历第一个结点一定是根结点

在这个二叉树中,
前序遍历的结果:M,G,D,B,A,C,F,E,J,H,I,K,L,S,P,O,N,Q,R,W,U,T,V,X,Z,Y
后序遍历的结果:A,C,B,E,F,D,I,H,L,K,J,G,N,O,R,Q,P,T,V,U,Y,Z,X,W,S,M
中序遍历的结果:A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z


函数柯里化

console.log(f(1).value); // 返回 1
console.log(f(1)(2).value); // 返回 5
console.log(f(1)(2)(3).value); // 返回 14
解析
function f(val, tamp = 0) {
  val = val * val + tamp;
  let fun = function (val2) {
    return f(val2, val);
  };
  fun.value = val;
  return fun;
}
console.log(sum(1) == 1);
console.log(sum(1)(2) == 3);
console.log(sum(1)(2)(3) == 6);
function sum() {
  let arr = [].slice.call(arguments);
  let fn = function () {
    let arr2 = [].slice.call(arguments);
    return sum.apply(null, arr.concat(arr2));
  };
  fn.toString = function () {
    return arr.reduce((v, n) => v + n);
  };
  return fn;
}

函数柯里化指的是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。

function curry(fn, args) {
  // 获取函数需要的参数长度
  let length = fn.length;
  args = args || [];
  return function () {
    let subArgs = args.slice(0);
    // 拼接得到现有的所有参数
    for (let i = 0; i < arguments.length; i++) {
      subArgs.push(arguments[i]);
    }
    // 判断参数的长度是否已经满足函数所需参数的长度
    if (subArgs.length >= length) {
      // 如果满足,执行函数
      return fn.apply(this, subArgs);
    } else {
      // 如果不满足,递归返回科里化的函数,等待参数的传入
      return curry.call(this, fn, subArgs);
    }
  };
}

// ES6 实现
function curry(fn, ...args) {
  return fn.length <= args.length ? fn(...args) : curry.bind(null, fn, ...args);
}

《JavaScript 专题之函数柯里化》


HTTP response status codes 404 502 403 500 504 304 200

解析

200 OK
301 Moved Permanently
302 Found
304 Not Modified
403 Forbidden
404 Not Found
500 Internal Server Error
502 Bad Gateway
504 Gateway Timeout


找出一个字符串出现次数最多的字符

let str = 'hajjfhlhhff';
解析
const max = (str) => {
  let obj = {}; // 先定义一个空对象,稍后使用。
  let maxChar = ''; // 要输出的字符
  let maxN = 0; // 字符数量
  [...str].map((k) => {
    if (obj[k]) {
      obj[k]++;
    } else {
      obj[k] = 1;
    }
    if (obj[k] > maxN) {
      maxN = obj[k];
      maxChar = k;
    }
  });
  console.log(` 字符串出现次数最多的字符:${maxChar} , 出现次数:${maxN}`);
};

max(str);

如何将浮点数点左边的数每三位添加一个逗号 (12000000.11 转化为 12,000,000.11)

解析
// 方法一
function format(number) {
  return number && number.replace(/(?!^)(?=(\d{3})+\.)/g, ',');
}
// 方法二
function format1(number) {
  return Intl.NumberFormat().format(number);
}
// 方法三
function format2(number) {
  return number.toLocaleString('en');
}

DOM 操作——怎样添加、移除、移动、复制、创建和查找节点?

解析
create a new div element
// and give it some content
var newDiv = document.createElement('div');
var newContent = document.createTextNode('Hi there and greetings!');
newDiv.appendChild(newContent); //add the text node to the newly created div.
  • 属性操作
getAttribute(key);
setAttribute(key, value);
hasAttribute(key);
removeAttribute(key);

详细资料可以参考:
《DOM 概述》
《原生 JavaScript 的 DOM 操作汇总》
《原生 JavaScript 中 DOM 节点相关 API 合集》


《如何更有效的获取文件扩展名》

解析

String.lastIndexOf() 方法返回指定值 (本例中的’.’) 在调用该方法的字符串中最后出现的位置,如果没找到则返回 -1.

对于 'filename''.hiddenfile' ,lastIndexOf 的返回值分别为 0-1 无符号右移操作符 (>>>) 将 -1 转换为 4294967295 , 将 -2 转换为 4294967294 , 这个方法可以保证边缘情况时文件名不变。

String.prototype.slice() 从上面计算的索引处提取文件的扩展名。如果索引比文件名的长度大,结果为 "".

function getFileExtension(filename) {
  return filename.slice(((filename.lastIndexOf('.') - 1) >>> 0) + 2);
}

常用正则表达式

解析
//(1) 匹配 16 进制颜色值
var color = /#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})/g;

//(2) 匹配日期,如 yyyy-mm-dd 格式
var date = /^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/;

//(3) 匹配 qq 号
var qq = /^[1-9][0-9]{4,10}$/g;

//(4) 手机号码正则
var phone = /^1[34578]\d{9}$/g;

//(5) 用户名正则
var username = /^[a-zA-Z\$][a-zA-Z0-9_\$]{4,16}$/;

//(6)Email 正则
var email = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;

//(7) 身份证号 (18 位) 正则
var cP =
  /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/;

//(8)URL 正则
var urlP = /^((https?|ftp|file):\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/;

// (9)ipv4 地址正则
var ipP =
  /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;

// (10)// 车牌号正则
var cPattern =
  /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领 A-Z]{1}[A-Z]{1}[A-Z0-9]{4}[A-Z0-9 挂学警港澳]{1}$/;

// (11) 强密码 (必须包含大小写字母和数字的组合,不能使用特殊字符,长度在 8-10 之间):
var pwd = /^(?=.\d)(?=.[a-z])(?=.[A-Z]).{8,10}$/;

详细资料可以参考:
《前端表单验证常用的 15 个 JavaScript 正则表达式》
《JavaScript 常用正则汇总》


正则表达式 ‘23 22’ => ‘2322’

解析
'23 22'.replace(/\s/g, '');

以字母开头,包含字母、数字、下划线的正则表达式

/^[a-zA-Z]\w+$/;

正则表达式以 4 位数字开头,结尾可能带有一个大写字母

const regex = /^[0-9]{4}[A-Z]?/g;

const str1 = '4444A';
const str2 = '4444';
const str3 = 'A4444';

console.log(regex.test(str1));
console.log(regex.test(str2));
console.log(regex.test(str3));

正则表达式



文章作者: 庄引
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 庄引 !
  目录