js面试题

原文地址: https://juejin.im/post/6844903869378461710

1 let暂时性死区

function sayHi() {
  console.log(name);
  console.log(age);
  var name = "Lydia";
  let age = 21;
}

sayHi(); // undefined 和 ReferenceError

let是否存在变量提升?

let name = 'happy';
{
  console.log(name); 
  // Uncaught ReferenceError: Cannot access 'name' before initialization
  let name = 'lucky';    
}
  • 使用var关键字声明的变量,会进行变量声明的提升,默认为undefined

  • 使用let(const)声明的变量,会有变量提升,but没有变量声明的提升,在声明它们之前,它们是不可访问的,被称为:暂时性死区。

变量赋值可以分为三个阶段:

  1. 创建变量,在内存中开辟空间;

  2. 初始化变量,将变量初始化为undefined;

  3. 真正赋值。

关于 let var function

  1. let的【创建】过程被提升了,但是初始化没有提升;

  2. var的【创建】和【初始化】都被提升了;

  3. function的【创建】【初始化】【赋值】都被提升了

2 let具有块作用域

for(var i=0; i<3; i++) {
  setTimeout(()=>console.log(i),1);  
}
for(let i=0; i<3; i++) {
  setTimeout(()=>console.log(i),1);  
}
  • var关键字声明的变量i是全局变量;当调用setTimeout函数时,i已经被赋值为3;

  • let(const)关键字声明的变量i是具有块作用域的(块是{}之间的任何东西);每次迭代期间,i将被创建为一个新值,并且每个值都会存在于循环内的块级作用域。

3 箭头函数

const shape = {
  radius: 10,
  diameter() {
    return this.radius*2;
  },
  perimeter: ()=> 2*Math.PI*this.radius
};
shape.diameter(); // 20
shape.perimeter(); // NaN
  • 对于箭头函数,this关键字指向是它所在上下文的环境,与普通函数不同!

4 静态方法

class Chameleon {
  static colorChange(newColor) {
    this.newColor = newColor;
  }
  constructor({newColor="green"}={}) {
    this.newColor = newColor;
  }
}
const freddie = new Chameleon({newColoe: "purple"});
freddie.colorChange("orange"); // TypeError
  • colorChange方法是静态的。静态方法仅在创建它们的构造函数中存在,并且不能传递给任何子级。

  • freddie是一个实例对象,函数不会传递,所以在freedie实例上不存在colorChange方法:抛出TypeError。

5 函数也是对象

function bark() {
  console.log('Woof!");
}
bark.animal = "dog"; // 这段代码没毛病!
  • 在js中,函数也是对象,原始类型之外的所有东西都是对象。

  • 函数是一种特殊类型的对象,该函数是具有属性的对象,此属性是可调用的。

6 构造函数&原型对象

function Person(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}
const member = new Person("Davi", "Chow");
Person.getFullName = () => this.firstName + this.lastName;
console.log(member.getFullName()); // TypeError
  • 不能像使用常规对象那样向构造函数添加属性,毕竟会浪费大量内存空间。

  • 如果要一次向所有对象添加功能,则必须使用原型:只需要将它放在内存中的一个位置,但实例都可以访问它。

Person.prototype.getFullName = function() {
  return `${this.firstName}` ${this.latName}`;
}

构造函数和原型对象的关系

  1. 每一个构造函数都有一个原型对象与之相关联

  2. 原型对象上面所有的成员(属性和方法)都可以被构造函数创建出来的(实例对象)所继承

  3. 构造函数Person 原型对象Person.prototype 实例对象p1

  4. 构造函数 --> 原型对象 构造函数.prototype

  5. 原型对象 --> 构造函数 原型对象.constructor

  6. 实例对象 --> 原型对象 实例对象.__proto__

访问原型对象的方法

  1. 构造函数.prototype
  2. 实例对象.__proto__
  3. Object.getPrototypeOf(实例对象)

原型链的基本结构

  1. 所有对象都是由构造函数创建出来的
  2. 所有的构造函数都有一个与之相关联的原型对象
  3. 原型对象也是对象 因此原型对象也是由构造函数创建出来的
  4. 原型对象的构造函数也有与之相关联的原型对象 因此原型对象的构造函数的原型对象也是对象
  5. 上面的对象也有构造函数...
  6. 以上就会形成一种链式的访问结构,称为原型链

对象访问规则

  • 就近原则:
    • 实例对象在访问成员时,先在自己身上查找,找到则直接使用
    • 如果没有就查找原型对象,如果在原型对象上找到,则直接使用
    • 原型对象上找不到就返回 undefined

7 构造函数&全局对象

function Person(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}
const p1 = new Person("Davi","Chow"); // Person {firstName:"Davi", lastName: "Chow"}
const p2 = Person("Davi","Chow"); // undefined
  • 不使用new创建对象时构造函数中的this指向的是全局对象,即global.firstName="Davi" global.lastName="Chow"

8 null

  • 所有对象都有原型 -- false -- 除null外的所有对象都有原型

9 加法

function sum(a,b) {
  return a + b;
}
sum(1, "2"); // 12

10 使用模板字符串传参

function f1(one, two, three) {
  console.log(one);
  console.log(two);
  console.log(three);
}
const person = "Davi";
const age = "18";

f1`${person} is ${age} years old`;
// ["", "is", "years old"]  Davi  18
  • 使用标记的模板字符串,则第一个参数的值始终是字符串值的数组。其余参数获取传递到模板字符串中的表达式的值。

11 typeof 复杂对象

function getAge(...args) {
  console.log(typeof args); // (...args)返回一个带参数的数组
}
getAge(21); // "object"

12 "use strict"; 时不声明变量

function f1() {
  "use strict";
  age = 18;
  console.log(age); 
}
 f1(); // ReferenceError

13 sessionStorage && localStorage

// cool_secret可以访问多长时间
sessionStorage.setItem("cool_secret", 123); 
// 关闭选项卡后,将删除存储在sessionStorage中的数据

// cool_secret2可以访问多长时间
localStorage.setItem("cool_secret2",123);
// 数据将永远存在,除非调用localStorage.clear()

14 对象存储 && set存储

const obj = { 1: 'a', 2: 'b', 3: 'c' };
const set = new Set([1, 2, 3, 4, 5]);
obj.hasOwnProperty('1'); // true
obj.hasOwnProperty(1); // true
set.has('1'); // false
set.has(1); // true
  • 所有对象键(不包括Symbols)都会被存储为字符串,即使没给定字符串类型的键

15 相同键名的对象

const obj = { a: 'one', b: 'two', a: 'three' };
console.log(obj); // {a: 'three', b: 'two'}

16 js全局执行上下文为你创建了两个东西:全局对象和this关键字

17 对象键 && 字符串

const a = {};
const b = { key: 'b' };
const c = { key: 'c' };
a[b] = 123; // const定义的对象能改变吗?
a[c] = 456;
console.log(a[b]); // 456
a[b.key] = 123;
a[c.key] = 456;
console.log(a[b.key]) // 123
a = { b:123, c: 456 }; // VM171:1 Uncaught TypeError: Assignment to constant variable.
  • console.log(a[b]); // 456

    • 对象键自动转换为字符串。当将一个对象设置为对象a的键,其值为123

    • 但是,当对象转为字符串时,它变成了[Object object]

    • 所以a[b] = a['Object object'] = 123 a[c] = a['Object object'] = 456

  • console.log(a[b.key]) // 123

    • 因为对象是引用类型的,a中保存的仅是对象的指针

    • 这就意味着,const仅保证指针不发生改变,修改对象的属性不会改变对象的指针,所以是被允许的。

    • 也就是说const定义的引用类型只要指针不发生改变,其他的不论如何改变都是允许的。

18 事件冒泡

<div onClick="console.log('first div')">
  <div onClick="console.log('second div')">
    <button onClick="console.log('button')">
      Click!
    </button>
  </div>
</div>
<!-- 单击按钮时event.target -> button -->
<!-- button  second div  first div -->
  • 导致事件的最深嵌套元素是事件的目标。可通过event.stopPropagation停止冒泡

  • 在事件传播期间,有三个阶段: 捕获,目标,冒泡 。在默认情况下,事件处理程序在冒泡阶段执行(除非useCature设置为true),从最深的嵌套元素向外延申。

19 call && bind

const person = { name: 'Davi' };
function sayHi(age) {
  console.log(`${this.name} is ${age}`);
}

sayHi.call(person, 12); // Davi is 12
sayHi.bind(person, 12); // function
  • callapply借用其他对象的方法并且绑定方法中的this,马上执行

  • bind方法绑定方法中的this,返回新的函数,不会立即执行

20 立即执行函数

function sayHi() {
  return (() => 0)();
}
typeof sayHi(); // "number"
  • sayHi函数返回立即调用的函数的返回值。该函数返回0,类型为数字。

21 假值 -- 下面哪些值是假值

0; // 假值
new Number(0); // 函数构造函数:真值
(""); // 假值
(" "); // 真值
new Boolean(false); // 函数构造函数:真值
undefined; // 假值
  • js中只有6个假值

  * undefined

  * null

  * NaN

  * 0

  * "" (empty string)

  * false

22 空插槽

const numbers = [1, 2, 3];
numbers[10] = 11;
console.log(numbers); // (11) [1, 2, 3, empty × 7, 11]
  • 当为数组中的元素设置一个超过数组长度的值时,js会创建一个名为“空插槽”的东西。这些位置的值实际上是undefined

23 作用域

(() => {
  let x, y;
  try {
    throw new Error();
  } catch(x) {
    (x = 1), (y = 2);
    console.log(x); // 1
  }
  console.log(x); // undefined
  console.log(y); // 2
})();
  • catch块接收参数x。传递的参数与变量x不同,catch块内的x是属于catch作用域的。

24 js中的所有内容都是原始类型和对象

  • js 只有原始类型和对象

  • 原始类型是 boolean, null, undefined, number, string, symbol

25 reduce

/*
* reduce((total, currentValue, currentIndex, arr)=>{}, initVal)
* 参数: 初始值|计算结束后的返回值  当前元素 
*/ 
[[0, 1], [2, 3]].reduce((acc, cur) => {
  return acc.concat(cur)
}, [1, 2]); 
// acc = [1, 2] cur = [0, 1] -> acc = [1, 2, 0, 1]
// acc = [1, 2, 0, 1] cur = [2, 3] -> acc = [1, 2, 0, 1, 2, 3]

26 setInterval的返回值

setInterval(() => console.log('Hi'), 1000);
// setInterval 返回一个唯一的id,此id可用于使用clearInterval()函数清除该定时器

27 扩展运算符

[..."Davi"];
// ["D", "a", "v", "i"]
  • 字符串是可迭代的,扩展运算符将迭代的每个字符映射到一个元素
posted on 2020-11-18 14:32  pleaseAnswer  阅读(119)  评论(0编辑  收藏  举报