1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > web前端面试高频考点——JavaScript 篇(一)【JS的三座大山 】 原型和原型链 作

web前端面试高频考点——JavaScript 篇(一)【JS的三座大山 】 原型和原型链 作

时间:2022-04-30 21:28:21

相关推荐

web前端面试高频考点——JavaScript 篇(一)【JS的三座大山 】 原型和原型链 作

系列文章目录

JavaScript 知识梳理,收录了web前端面试95%以上的高频考点,满满的干货。给你做一个高效的知识梳理,为你的面试保驾护航!

文章目录

系列文章目录变量类型和计算JS 值类型和引用类型的区别值类型引用类型常见值类型(基本数据类型)常见引用类型 手写深拷贝typeof 运算符手撕深拷贝 变量计算 - 类型转换 原型和原型链class 类继承JS原型(隐式原型和显式原型)类型判断 instanceof原型隐式原型和显式原型原型关系基于原型的执行规则 原型链hasOwnProperty 手写简易 jQuery 作用域和闭包作用域自由变量闭包this手写 bind实际开发中闭包的应用 异步同步和异步的不同单线程和异步异步和同步 应用场景异步相关面试题手写 Promise 加载一张图片

变量类型和计算

JS 值类型和引用类型的区别

值类型

let a = 100let b = aa = 200console.log(b) // 100

出处:/lesson/400.html#mid=30282

引用类型

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

出处:/lesson/400.html#mid=30282

常见值类型(基本数据类型)

字符串(String)数字(Number)布尔(Boolean)未定义(Undefined)空(Null)Symbol

常见引用类型

Object 类型Array 类型Function 类型Date 类型RegExp 类型

手写深拷贝

typeof 运算符

能判断所有的值类型能判断函数能识别引用类型(不能再继续识别)

示例 1:判断所有的值类型(基本数据类型)

let a = 'hello'let b = 11let c = truelet dlet s = Symbol('hi')console.log(typeof a) // stringconsole.log(typeof b) // numberconsole.log(typeof c) // booleanconsole.log(typeof d) // undefinedconsole.log(typeof s) // symbol

示例 2:能判断函数

let x = console.loglet y = function() {}console.log(typeof x) // functionconsole.log(typeof y) // function

示例 3:能识别引用类型(不能再继续识别)

console.log(typeof null) // objectconsole.log(typeof ['hello']) // objectconsole.log(typeof {age: 20}) // object

手撕深拷贝

普通写法:

const obj1 = {age: 20,name: '张三',address: {city: '北京'},arr: ['x', 'y', 'z']}const obj2 = deepClone(obj1)obj2.address.city = '上海'obj2.arr[0] = 'a'console.log(obj1.address.city) // 北京console.log(obj1.arr[0]) // x// 深拷贝function deepClone(obj = {}) {if(typeof obj !== 'object' || obj == null) {// obj 是 null,或者不是对象和数组,直接返回return obj}// 初始化返回结果let resultif(obj instanceof Array) {result = []} else {result = {}}for(let key in obj) {// 保证 key 不是原型的属性if(obj.hasOwnProperty(key)) {// 递归result[key] = deepClone(obj[key])}}// 返回结果 return result}

简写形式:

// 深拷贝函数function deepClone(obj) {// 1 判断是否是非引用类型或者nullif (typeof obj !== 'object' || obj == null) return obj// 2 创建一个容器let result = new obj.constructor()// 3 拿到对象的keys,给容器赋值Object.keys(obj).forEach(v => result[key] = deepClone(obj[key]))// 4 返回容器return result}

变量计算 - 类型转换

字符串拼接==if 语句逻辑运算

示例 1:字符串拼接

const a = 100 + 10 // 110const b = 100 + '10' // '10010'const c = true + '10' // 'true10'

示例 2:==

100 == '100' // true0 == '' // true0 == false // truefalse == '' // truenull = undefined // true

示例 2 扩展:

除了==null 之外,其他都用===

const obj = {name: 'zhangsan'}if (obj.age == null) {}// 相当于:if (obj.age === null || obj.age === undefined) {}

示例 3:truly 变量和 falsely 变量

truly 变量:!!a == true的变量falsely 变量:!!a === false的变量

以下是 falsely 变量,除此之外都是 truly 变量

!!0 === false!!NaN === false!!'' === false!!null === false!!undefined == false!!false === false

示例 4:逻辑判断

注:10 是 truly 变量,继续往后判断返回第二个值

console.log(10 && 0) // 0console.log('' || 'abc') // 'abc'console.log(!window.abc) // true

原型和原型链

class 类

constructor属性方法

示例:

// 学生类class Student {constructor(name, number) {this.name = namethis.number = number}sayHi() {console.log(`姓名 ${this.name}, 学号 ${this.number}`);}}// 通过类 new 对象/实例const xialu = new Student('夏洛', '')console.log(xialu.name) // 夏洛console.log(xialu.number) // xialu.sayHi() // 姓名 夏洛, 学号

继承

extendssuper扩展或重写方法

示例:子类继承父类

// 父类class People {constructor(name) {this.name = name}eat() {console.log(`${this.name} eat food`);}}// 子类class Student extends People {constructor(name, number) {super(name)this.number = number}sayHi() {console.log(`姓名 ${this.name} 学号 ${this.number}`);}}// 子类class Teacher extends People {constructor(name, major) {super(name)this.major = major}teach() {console.log(`${this.name} 教授 ${this.major}`)}}// 实例const xialuo = new Student('夏洛', '')console.log(xialuo.name) // 夏洛console.log(xialuo.number) // xialuo.sayHi() // 姓名 夏洛, 学号 xialuo.eat() // 夏洛 eat food// 实例const wanglaoshi = new Teacher('王老师', '语文')console.log(wanglaoshi.name) // 王老师console.log(wanglaoshi.major) // 语文wanglaoshi.teach() // 王老师 教授 语文wanglaoshi.eat() // 王老师 eat food

JS原型(隐式原型和显式原型)

类型判断 instanceof

console.log(xialuo instanceof Student) // trueconsole.log(xialuo instanceof People) // trueconsole.log(xialuo instanceof Object) // true

原型

class 实际上是函数

console.log(typeof Student) // 'function'console.log(typeof Teacher) // 'function'console.log(typeof People) // 'function'

隐式原型和显式原型

隐式原型:__prop__显式原型:prototype

实例对象的隐式原型等于对应构造函数的显示原型

console.log(xialuo.__proto__) // People {constructor: ƒ, sayHi: ƒ}console.log(Student.prototype) // People {constructor: ƒ, sayHi: ƒ}console.log(xialuo.__proto__ === Student.prototype) // true

出处:/lesson/400.html#mid=30288

原型关系

每个 class 都有一个显式原型prototype每个实例都有隐式原型__proto__实例的__proto__指向对应 class 的prototype

基于原型的执行规则

获取属性 xialuo.name 或执行方法 xialuo.sayHi 时先在自身属性和方法寻找如果找不到则自动去__proto__中查找

原型链

instanceof 顺着隐式原型往上找,找到返回 true,找不到返回 false

出处:/lesson/400.html#mid=30289

hasOwnProperty

hasOwnProperty 会查找一个对象是否有某个属性,但是不会去查找它的原型链

console.log(xialuo.hasOwnProperty('name')) // trueconsole.log(xialuo.hasOwnProperty('eat')) // false

手写简易 jQuery

通过 class类 和 原型,手写 jQuery 部分功能

<p>第一段文字</p><p>第二段文字</p><p>第三段文字</p>class jQuery {constructor(selector) {const result = document.querySelectorAll(selector)const length = result.length for (let i = 0; i < length; i++) {this[i] = result[i]}this.length = lengththis.selector = selector}get(index) {return this[index]}each(fn) {for (let i = 0; i < this.length; i++) {const elem = this[i]fn(elem)}}on(type, fn) {return this.each(elem => {elem.addEventListener(type, fn, false)})}// 扩展很多 DOM API}// 插件jQuery.prototype.dialog = function (info) {alert(info)}// “造轮子”class myJQuery extends jQuery {constructor(selector) {super(selector)}// 扩展自己的方法addClass(className) {}style(data) {}}

测试:

作用域和闭包

作用域

全局作用域函数作用域块级作用域

全局作用域:如 window 对象、document 对象

函数作用域:只能在函数里面使用

块级作用域:在块内有效

示例:全局作用域

在任何地方都能获取到

window.a = 'zhangsan'function fn() {console.log(window.a)}fn() // zhangsan

示例:函数作用域

里面的函数能读取到外面函数的变量

function fn1() {let a = 'zhangsan'function fn2() {let b = 'lisi'console.log(a)}fn2()}fn1() // zhangsan

外面的函数不能读取里面函数的变量

function fn1() {let a = 'zhangsan'console.log(b)function fn2() {let b = 'lisi'}fn2()}fn1() // 报错:b is not defined

示例:块级作用域

在块之外读取不到变量

if (true) {let x = 100}console.log(x) // 报错:x is not defined

示例:创建 10 个<a>标签,点击的时候弹出来对应的序号

let afor(let i = 0; i < 10; i++) {a = document.createElement('a')a.innerHTML = i + '<br>'a.addEventListener('click', function (e) {e.preventDefault()alert(i)})document.body.appendChild(a)}

自由变量

一个变量在当前作用域没有定义,但被使用了向上级作用域,一层一层依次寻找,直至找到为止如果到全局作用域都没找到,则报错 xx is not defined

示例:不在当前作用域的就一层层往上找,a、b 都要往上层找

let a = 1function fn1() {let b = 2function fn2() {let c = 3console.log(a + b + c)}fn2()}fn1() // 6

闭包

作用域应用的特殊情况,有两种表现:

函数作为返回值被返回函数作为参数被传递

总结:所有自由变量的查找,是在函数定义的地方,向上级作用域查找,不是执行的地方

示例:函数作为返回值

function create() {const a = 100return function() {console.log(a)}}const fn = create()const a = 200fn() // 100

示例:函数作为参数

function print(fn) {const a = 200fn()}const a = 100function fn() {console.log(a)}print(fn) // 100

this

作为普通函数:指向 window使用 call apply bind :传入什么绑定什么(call、apply、bind的区别)作为对象方法被调用:指向当前对象本身,异步指向 window,箭头函数的异步指向上级作用域在 class 方法中调用:指向当前实例本身箭头函数:取上级作用域 this 的值

注:this 的取值是在函数执行的时候确定的,不是在函数定义的时候确定的

示例 1:普通函数、使用 call、apply、bind

function fn1() {console.log(this)}fn1() // window

示例 2:call、apply、bind 指定指向

function fn1() {console.log(this)}fn1() // windowfn1.call({x: 100}) // {x: 100}const fn2 = fn1.bind({x: 200})fn2() // {x: 200}

示例 3:作为对象方法被调用、箭头函数

const zhangsan = {name: 'zhangsan',sayHi() {console.log(this)},wait() {setTimeout(function() {console.log(this)})},waitAgain() {setTimeout(() => {console.log(this)})}}zhangsan.sayHi() // this即当前对象zhangsan.wait() // this === windowzhangsan.waitAgain() // this即当前对象

示例 4:在 class 方法中调用

class People {constructor(name) {this.name = name}sayHi() {console.log(this)}}const zhangsan = new People('张三')zhangsan.sayHi() // this 指向张三对象

手写 bind

在函数原型上添加 bind1 方法,模拟 bind将参数拆解为数组通过 shift 方法挖走数组的第一项作为 this把 this 赋值给 self,指向调用 bind 方法的函数最后返回一个函数

// 模拟 bindFunction.prototype.bind1 = function() {// 将参数拆解为数组// const args = Array.prototype.slice.call(arguments)const args = Array.from(arguments)// 获取 this (数组第一项)const t = args.shift()// fn1.bind(...) 中的 fn1const self = this// 返回一个函数return function () {return self.apply(t, args)}}function fn1(a, b, c) {console.log('this', this)console.log(a, b, c)return 'this is fn1'}const fn2 = fn1.bind1({x: 100}, 10, 20, 30)const res = fn2()console.log(res)

实际开发中闭包的应用

隐藏数据如做一个简单的 cache 工具

// 闭包隐藏数据,只提供 APIfunction createCache() {const data = {} // 闭包中的数据,被隐藏,不被外界访问return {set: function (key, val) {data[key] = val},get: function (key) {return data[key]}}}const c = createCache()c.set('a', 100)console.log(c.get('a')) // 100

异步

同步和异步的不同

单线程和异步

JS 是单线程语言,只能同时做一件事浏览器和 nodejs 已支持 JS 启动进程,如 Web WorkerJS 和 DOM 渲染共用同一个线程,因为 JS 可修改 DOM 元素遇到等待(网络请求,定时任务)不能卡住需要异步回调 callback 函数形式

异步和同步

基于JS是单线程语言异步不会阻塞代码执行同步会阻塞代码执行

异步:callback 回调函数,等主线程任务执行完再执行

console.log(100)setTimeout(() => {console.log(200)}, 100)console.log(300)// 输出顺序:100 300 200

同步:按顺序执行,前面没执行完后面的不会执行

console.log(100)alert(200)console.log(300)

应用场景

网络请求,如 ajax、图片加载定时任务,如 setTimeout

ajax:

console.log('start')$.get('xxx.json', function (data1) {console.log(data1)})console.log('end')

图片加载:

console.log('start')let img = document.createElement('img')img.onload = function () {console.log('loaded')}img.src = 'xxx.png'console.log('end')

setTimeout:

console.log('start')setTimeout(function() {console.log('异步')}, 100)console.log('end')

异步相关面试题

手写 Promise 加载一张图片

function loadImg(src) {const p = new Promise((resolve, reject) => {const img = document.createElement('img')img.onload = () => {resolve(img)}img.onerror = () => {const err = new Error(`图片加载失败 ${src}`)reject(err)}img.src = src // src 一赋值,立马触发图片的加载document.body.appendChild(img)})return p}const url1 = 'xxx.png'const url2 = 'xx.png'loadImg(url1).then(img1 => {console.log(img1.width)return img1 // 普通对象}).then(img1 => {console.log(img1.height) return loadImg(url2) // promise 实例}).then(img2 => {console.log(img2.width)return img2}).then(img2 => {console.log(img2.height)}).catch(error => console.err(error))

不积跬步无以至千里 不积小流无以成江海

点个关注不迷路,持续更新中…

web前端面试高频考点——JavaScript 篇(一)【JS的三座大山 】 原型和原型链 作用域和闭包 异步

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。