面试自我知识点随记

let、const 以及 var 的区别是什么?

  • let 和 const 定义的变量不会出现变量提升,而 var 定义的变量会提升。
  • let 和 const 是JS中的块级作用域( {} )
  • let 和 const 不允许重复声明(会抛出错误)
  • let 和 const 定义的变量在定义语句之前,如果使用会抛出错误(形成了暂时性死区),而 var 不会。
  • const 声明一个只读的常量。一旦声明,常量的值就不能改变(如果声明是一个对象,那么不能改变的是对象的引用地址)

Tip: 暂时性死区

在代码块内,使用 let/const 命令声明变量之前,该变量都是不可用的(会抛出错误)。这在语法上,称为“暂时性死区”。暂时性死区也意味着 typeof 不再是一个百分百安全的操作

typeof x; // ReferenceError(暂时性死区,抛错): x is not defined
let x;

typeof y; // 值是undefined,不会报错

暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。

(伪)类数组可以转换为数组:

类数组是一个普通对象,而真实的数组是Array类型。

常见的类数组有:

  • 函数的参数 arguments,
  • DOM 对象列表(比如通过 document.querySelectorAll 得到的列表),
  • jQuery 对象 (比如 $(“div”))
//第一种方法
[...arrayLike];
//第二种方法
Array.from(arrayLike);
//第三种方法:
Array.prototype.slice.call(arrayLike, start);

new 关键字做了什么?

  1. 创建了一个新对象,并且this变量引用了该对象,新对象同时还继承了构造函数的原型
  2. 属性和方法被this加入到所引用的新对象中(重新绑定this,使构造函数的this指向新对象 )
  3. 新创建的对象由 this 所引用,最后隐式的返回this(return this),新创建的对象就拥有了构造函数的属性和方法了

原型链

当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,它就会去它的原型(prototype)上去找这个属性,这个原型上没有的话,(原型也是一个对象)就会去原型的原型上去找,以此类推。最后找到Object,如果Object.prototype上也没有的话,那就是null

原型链是实现继承的主要方法

用原生js(构造函数)写继承

function Person(name, age) {
  this.name = name
  this.age = age
}
Person.prototype.sayHi = function() {
  console.log(this)
}
function man(name, age, sex) {
  Person.call(this, name, age)
  this.sex = sex
}
man.prototype.sing = function() {
  console.log(this.name, this.age, this.sex)
}
// man.prototype = new Person() 语义化不强,不适合
man.prototype = Object.create(Person.prototype) 
// Object.create(protoObj) 会创建一个新对象,并且这个新对象的prototype会指向protoObj

man.prototype.constructor = man // 我们需要手动定义man.prototype的构造器,指回构造函数本身

var zs = new man('zs',18,'男')
// zs => man {name: "zs", age: 18, sex: "男"}

zs.__proto__ === man.prototype // true

man.prototype.__proto__ === Person.prototype // true

Person.prototype.__proto__ === Object.prototype // true

Object.prototype.__proto__ === null // true

Tip

man.prototype = Object.create(Person.prototype) 
man.prototype.constructor = man

// => 以上代码等价于以下
man.prototype = Object.create(Person.prototype, {
  constructor: {
    value: man,
    enumerable: false,
    writable: false,
    configurable: false
  }
});

面试题

Object.prototype.__proto__ // null
Function.prototype.__proto__ // Object.prototype
Object.__proto__ // Function.prototype

以上涉及Function的原型问题:

ES6类的继承

Tip :

  • ES6中类没有变量提升
  • 通过构造函数创建实例,是可以变量提升的。 es6中的类,必须先有类,才可以实例化
  • constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加
class Father {
  constructor(name){
     this.name = name
  }
  sing() {
     return this.name + '我在唱歌'
  }
}

// ES6中用 extends 关键字继承
class son extends Father {
  constructor(name,score) {
    super(name)
    this.score = score
  }
  doing() {
    return this.name + ',' + this.sing()
  }
}

let ldh = new son('刘德华',99)

ldh
// => son {name: "刘德华", score: 99}
ldh.doing()
// => "刘德华,刘德华我在唱歌"

类和构造函数的区别

  1. 类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行。
  2. 的所有实例共享一个原型对象**。
  3. 类的内部,默认就是严格模式,所以不需要使用use strict指定运行模式。
构造函数特点:
  1. 构造函数有原型对象prototype。
  2. 构造函数原型对象prototype里面有constructor,指向构造函数本身。
  3. 构造函数可以通过原型对象添加方法。
  4. 构造函数创建的实例对象有__proto__原型,指向构造函数的原型对象。
类的特点:
  1. class本质还是function
  2. 类的所有方法都定义在类的prototype属性上
  3. 类创建的实例,里面也有__proto__指向类的prototype原型对象
  4. 新的class写法,只是让对象原型的写法更加清晰,更像面向对象编程的语法而已。
  5. ES6的类其实就是语法糖

Tip:

什么是语法糖?加糖后的代码功能与加糖前保持一致,糖在不改变其所在位置的语法结构的前提下,实现了运行时的等价。

语法糖没有改变语言功能,但增加了程序员的可读性。

基本数据类型和复杂数据类型的区别:

1.内存分配不同

  • 基本数据类型存储在中。
  • 复杂数据类型存储在中,栈中存储的地址,是指向堆中的引用地址,属性及值存储在堆中。

2.访问机制不同

  • 基本数据类型是按值访问

  • 复杂数据类型是按引用访问,JS不允许直接访问保存在堆内存中的对象,在访问一个对象时,首先得到的是这个对象在栈内存中的地址,然后按照这个地址去获得存储在堆中的值。

  • a.基本数据类型作为参数时,修改基本数据类型的参数不影响原有数据

    b.复杂数据类型,是通过引用地址来访问的,所以作为参数时,修改属性后,会影响原有数据

let b = { age: 10 }

let a = b;
a.age = 20;
console.log(a); // { age: 20 }
console.log(b); // { age: 20 }

基本数据类型:
//基本数据类型
let b = 10

function change(info) {
    info=20;
}
// info=b;基本数据类型,拷贝的是值得副本,二者互不干扰
change(b);
console.log(b);//10
复杂数据类型:
//复杂数据类型
let b = {
    age: 10
}

function change(info) {
    info.age = 20;
}
//info=b;根据第三条差异,可以看出,拷贝的是地址的引用,修改互相影响。
change(b);
console.log(b);//{ age: 20 }

Tip:

  • 存储的时候,所有的简单类型都存放在栈中,所有的复杂类型的内容多存在堆中。
  • 复杂类型的值虽然存在堆中,但是他会有一个地址存在栈中。
  • 可以由这个栈里面的地址找到对应的堆中的复杂类型的数据

Boolean

该抽象操作负责处理非布尔值到布尔值转换.

typeresult
nullfalse
undefinedfalse
boolean不转换
string“” => false; 其它 => true
number+0, −0, NaN => false; 其它 => true
Objecttrue

真值 & 假值
假值(强制类型转换false的值) => undefined, null, false, +0, -0, NaN, "".
真值(强制类型转换true的值) => 除了假值, 都是真值.

隐式强制类型转换

+/ -/ ! / ~
  1. +/- 一元运算符 => 运算符会将操作数进行ToNumber处理.
  2. ! => 会将操作数进行ToBoolean处理.
  3. ~ => (~x)相当于 -(x + 1) eg: ~(-1) ==> 0; ~(0) ==> 1; 在if (…)中作类型转换时, 只有-1时, 才为假值.
  4. +加号运算符 => 若操作数有String类型, 则都进行ToString处理, 字符串拼接. 否则进行ToNumber处理, 数字加法.
条件判断
  1. if (...), for(;;;), while(...), do...while(...)中的条件判断表达式.
  2. ? : 中的条件判断表达式.
  3. ||&& 中的中的条件判断表达式.

以上遵循Boolean规则

||(逻辑‘或’) 和 &&(逻辑’与’)
  1. 返回值是两个操作数的中的一个(且仅一个). 首先对第一个操作数条件判断, 若为非布尔值则进行ToBoolean强制类型转换.再条件判断.
  2. || => 条件判断为true, 则返回第一个操作数; 否则, 返回第二个操作数. 相当于 a ? a : b;
  3. && => 条件判断为true, 则返回第二个操作数; 否则, 返回第一个操作数, 相当于 a ? b : a;

onscroll onblur onfocus onmouseenter onmouseleave 没有事件冒泡 不能进行事件委托

ECMAScript5标准下,严格模式中匿名函数的的this指向的是undefined,不是window

用了严格模式**“use strict”**,严格模式下无法再意外创建全局变量,所以this不为window而为undefined

<html>
<script type="text/javascript">
    "use strict";  
    var foo = function foo(){
        console.log(this)
    };
    foo(); //undefined
</script>
</html>
严格模式为什么对箭头函数没有效果,返回还是window

Given that this comes from the surrounding lexical context, strict mode rules with regard to this are ignored.

lexical means that this refers to the this value of a lexically enclosing function.

综上所述,在箭头函数中,thislexical类型,lexical意味着这个this指是所在封闭函数中this,所以严格模式会自动忽视use strict,所以this如下所示:

<html>
<script type="text/javascript">
var foo = () => {
    "use strict";
    console.log(this)
};
foo(); //Window
</script>
</html>

箭头函数中,this指向运行时所在的对象,而use strict被移到函数内了,所以this为全局变量window

函数中的 this指向问题

  • 普通函数:window
  • 对象方法:该方法所属对象
  • 通过new关键字,构造函数&构造函数.prototype上的方法:实例
  • 事件绑定:事件源
    • 事件三要素:事件源,事件类型,事件处理函数
    • 事件流:捕获、目标、冒泡
  • 定时器:window
  • 自执行函数:window
  • 箭头函数: 箭头函数没有自己的this,箭头函数中的this继承于外层代码库中的this.

js执行机制

js执行的时候,一定是优先执行所有的同步代码,等待所有的同步代码执行完毕之后,再把异步代码放进来执行

让页面一加载,就会把所有的代码分开,同步代码都放到执行栈中执行,异步代码放到任务队列

执行栈(放同步代码地方)(所有的代码都放到执行栈中执行)

任务队列(放异步代码的地方)

数组的哪些API会改变原数组

(变异方法)修改原数组的API有:

push pop unshift shift splice reverse sort fill copyWithin

(非变异方法)不修改原数组的API有:

slice map forEach filter reduce entries find findIndex includes concat some every

**添加到 . e x t e n d ( ) 上 的 是 全 局 函 数 : ∗ ∗ .extend( ) 上的是全局函数:** .extend().xxx 调用

添加到jQuery.fn.extend( )上的是原型对象上的函数,需要通过jQuery的对象来调用 $().xxx调用

什么是闭包?闭包的作用是什么?闭包有哪些使用场景?

闭包是指有权访问另一个函数作用域中的变量的函数,创建闭包最常用的方式就是在一个函数内部创建另一个函数。

闭包的作用有:

  1. 封装私有变量
  2. 模仿块级作用域(ES5中没有块级作用域)
  3. 实现JS的模块

内存泄漏

内存泄露是指一块被分配的内存既不能使用,又不能回收,直到浏览器进程结束

浏览器中也是采用自动垃圾回收方法管理内存,但由于浏览器垃圾回收方法有 bug,会产生内存泄露。

  1. 闭包会造成内存泄漏
  2. 全局变量引起的内存泄漏
  3. 监听事件添加后,没有移除

防抖和节流的区别是什么?

防抖和节流的作用都是防止函数多次调用。区别在于,假设一个用户一直触发这个函数,且每次触发函数的间隔小于设置的时间,防抖的情况下只会调用一次,而节流的情况会每隔一定时间调用一次函数。

防抖(debounce): n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间

防抖的应用场景:

  • 每次 resize/scroll 触发统计事件
  • 文本输入的验证(连续输入文字后发送 AJAX 请求进行验证,验证一次就好)

节流(throttle): 高频事件在规定时间内只会执行一次,执行一次后,只有大于设定的执行周期后才会执行第二次。

函数节流的应用场景有:

  • DOM 元素的拖拽功能实现(mousemove)
  • 射击游戏的 mousedown/keydown 事件(单位时间只能发射一颗子弹)
  • 计算鼠标移动的距离(mousemove)
  • Canvas 模拟画板功能(mousemove)
  • 搜索联想(keyup)
  • 监听滚动事件判断是否到页面底部自动加载更多:给 scroll 加了 debounce 后,只有用户停止滚动后,才会判断是否到了页面底部;如果是 throttle 的话,只要页面滚动就会间隔一段时间判断一次

为什么 0.1 + 0.2 != 0.3 ?

0.1 + 0.2 != 0.3 是因为在进制转换和进阶运算的过程中出现精度损失 TIp

=====的区别

  • ===:严格等于,会先比较两边的类型,只有类型相同,才会比较数据
  • ==:普通等于,如果两边的类型不同,会先进行隐式转换,再比较数据
  • ===== 性能高一丢丢,占用的资源少一丢丢,因为不存在隐式转换,效率会高一些,所以推荐使用严格等于:===

实例方法与静态方法

  • 实例方法,是通过new关键字创建出来的实例调用的方法
  • 静态方法,不需要new实例,可以直接通过构造函数调用的方法

get 和 post的区别

  1. get用来获取数据,post用来提交数据
  2. get参数通过URL传递,post放在请求体中
  3. get在浏览器回退时是无害的,而post会提示你,再次提交表单请求
  4. get请求在URL中传送的参数是有长度限制的(一般限制在 2~8K 之间),而post的数据则可以非常大(PHP 默认是 2M)
  5. post比get安全,因为get参数直接暴露在URL上,所以不能用来传递敏感信息
  6. get请求只能进行url编码,而post支持多种编码方式
  7. get请求会保存在浏览器历史记录中,还可能保存在web服务器的日志中

一个页面从输入 URL 到页面加载显示完成,这个过程中都发生了什么?

1、浏览器地址栏输入url

2、浏览器会先查看浏览器缓存–系统缓存–路由缓存,如有存在缓存,就直接显示。如果没有,接着第三步

3、域名解析(DNS)获取相应的ip

4、浏览器向服务器发起tcp连接,与浏览器建立tcp三次握手

5、握手成功,浏览器向服务器发送http请求,请求数据包

6、服务器请求数据,将数据返回到浏览器

7、浏览器接收响应,读取页面内容,解析html源码,生成DOm树

8、解析css样式、浏览器渲染,js交互绑定多个域名,数量不限;

iframe

a>通过iframe实现跨域;
b>使用iframe解决IE6下select遮挡不住的问题
c>通过iframe解决Ajax的前进后退问题
d>通过iframe实现异步上传。(Easyui中form组件就是用的iframe,实现表单提交时,可以提交上传域)

小程序生命周期

es6语法

  1. let const
  2. 箭头函数
  3. 解构赋值
  4. 反引号 xxx+${变量}
  5. promise
  6. 剩余参数 和扩展运算符
  7. async await

组件化开发优点

  1. 提高开发效率
  2. 方便重复使用
  3. 简化调试步骤
  4. 提升整个项目的可维护性
  5. 便于协同开发
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值