资讯详情

前端面试题

面试题

1.闭包篇

1.1作用域

作用域是确定在哪里以及如何找到变量标识符的规则的一套规则。(即找到变量的地方)

1.2 作用域链

函数之间的嵌套形成了作用域链。

1.3 词法作用域

词法作用域 是 作用域的工作模式。

在编写代码时,变量和块作用域写在哪里,即词法作用域是静态作用域,写代码时确定。

1.4 闭包的概念

即使函数在当前的词法作用域之外执行,当函数能够记住并访问其词法作用域时,也会产生闭包。

1.5 可能出现闭包问题:

(1)内存泄漏 解决:使用delete删除

1.6 闭包的作用

(1) 定义模块 (2)立即执行函数 (3)存储数据

2. 防抖与节流

2.1 函数防抖

定义: 如果在事件触发N秒后进行回调 n 秒内再次触发,重新计时

理解:就像法术技能一样 要读条,技能读条还没完成,再按技能再读条。

用于:搜索框

2.2 函数节流

定义: 在规定的单位时间内,函数只能触发一次。若该单位在时间内多次触发函数,则只能生效一次

理解:

3.跨域(非同源请求策略)

方式1:jsonp

原理:利用script不受同源策略的影响

像script、img、link、iframe 等 不受同源策略的影响。

[外链图片转存失败,源站可能有防盗链机制,建议保存图片并直接上传(img-sa0pO1Hf-1639491671619)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210614230945302.png)]

问题:jsonp只能处理get请求

代码:

客户端    function jsonp({ url, params, callback }) {        return new Promise((resolve, reject) => {         let script = document.createElement('script')          window[callback] = function (data) {           resolve(data)           document.body.removeChild(script)           //为了执行此函数,就将script这个元素给删了         }          let arr = []         // 对象的克隆         params = { ...params, callback }          for (let key in params) {           arr.push(`${key}=${params[key]}`)         }          script.src = `${url}?${arr.join('&')}`         document.body.appendChild(script)       })       }     jsonp({       url: 'http://localhost:3000/say',       params: { wd: 'Ilovety' },       callback: 'show'     }).then(data => {       console.log(data)     })               服务端:          const express = require('express')  let app = express()  app.get('/say',function(req,res){   let {wd,callback} = req.query    console.log(wd)    console.log(callback)   res.send(`${callback}(我也爱你)`)  })  app.listen(3000,function(){   console.log('3000 is running !') })  

方式2:CORS跨境资源共享

1.客户端(发送ajax/fetch 请求)

2.服务端设置中间键

缺点:如果设置为所有源,则此res.header(“Access-Control-Allow-Origin”, “*”);就不可以携带cookie,若设置可携带cookie只能允许一个访问源。

方式3:http proxy

[外链图片转存失败,源站可能有防盗链机制,建议保存图片并直接上传(img-8GENACrw-1639491671621)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210614234326691.png)]

port:端口号

progress:显示加载进度

contentBase:目录

方式4:ngnix反向代理

方式5:postMessage

[外链图片转存失败,源站可能有防盗链机制,建议保存图片并直接上传(img-RA8I6bVv-1639491671621)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210616110534520.png)]

[外链图片转存失败,源站可能有防盗链机制,建议保存图片并直接上传(img-iV2XSKuS-1639491671622)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210616110509753.png)]

postMessage为数不多的跨域操作window其中一个属性可以用来解决:

  1. 打开新窗口的页面和数据传输

  2. 多窗口之间的信息传递

  3. 页面和嵌套iframe消息传递。

message:将发送到其他地方 window 的数据

targetOrigin:允许这些窗口访问。其值可以是字符串*(表示无限制)或URL。不匹配是不可接受的。

transfer(可选):是一串和message 同时传递的 Transferable 对象. 这些对象的所有权将转移给消息的接收人,发送人将不再保有所有权。

// a.html   <iframe src="http://localhost:4000/b.html" frameborder="0" id="frame" onload="load()"></iframe> //   //内嵌在http://localhost:3000/a.html     <script>       function load() {         let frame = document.getElementById('frame')         frame.contentWindow.postMessage(我爱你, 'http://localhost:4000') ///发送数据         window.onmessage = function(e) { //接收返回数据           console.log(e.data) 我不爱你         }       }     </script>     // b.html   window.onmessage = function(e) {     console.log(e.data) //我爱你     e.source.postMessage(我不爱你, e.origin)  }  

方式6 : document.domain iframe

适用范围:适用于主域相同,子域不同的情况下

[外链图片存储失败,源站可能有防盗链机制,建议保存图片直接上传(img-sL6e16m1-1639491671623)(C:\Users\Administrator\AppData\Roaming\Tyora\typora-user-images\image-20210616124920462.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tiRN9d8j-1639491671625)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210616124934861.png)]

7.node中间件代理

实现原理:

需要做到以下几个步骤:

  1. 接受客户端请求
  2. 将请求 转发给服务器
  3. 拿到服务器 响应数据
  4. 将响应转发给客户端

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XX53odYR-1639491671626)(https://user-gold-cdn.xitu.io/2019/1/17/1685c5bed77e7788?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)]

我们先来看个例子:本地文件index.html文件,通过代理服务器http://localhost:3000向目标服务器http://localhost:4000请求数据。

// index.html(http://127.0.0.1:5500)
 <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
    <script>
      $.ajax({
        url: 'http://localhost:3000',
        type: 'post',
        data: { name: 'xiamen', password: '123456' },
        contentType: 'application/json;charset=utf-8',
        success: function(result) {
          console.log(result) // {"title":"fontend","password":"123456"}
        },
        error: function(msg) {
          console.log(msg)
        }
      })
     </script>

// server1.js 代理服务器(http://localhost:3000)
const http = require('http')
// 第一步:接受客户端请求
const server = http.createServer((request, response) => {
  // 代理服务器,直接和浏览器直接交互,需要设置CORS 的首部字段
  response.writeHead(200, {
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Methods': '*',
    'Access-Control-Allow-Headers': 'Content-Type'
  })
  // 第二步:将请求转发给服务器
  const proxyRequest = http
    .request(
      {
        host: '127.0.0.1',
        port: 4000,
        url: '/',
        method: request.method,
        headers: request.headers
      },
      serverResponse => {
        // 第三步:收到服务器的响应
        var body = ''
        serverResponse.on('data', chunk => {
          body += chunk
        })
        serverResponse.on('end', () => {
          console.log('The data is ' + body)
          // 第四步:将响应结果转发给浏览器
          response.end(body)
        })
      }
    )
    .end()
})
server.listen(3000, () => {
  console.log('The proxyServer is running at http://localhost:3000')
})

// server2.js(http://localhost:4000)
const http = require('http')
const data = { title: 'fontend', password: '123456' }
const server = http.createServer((request, response) => {
  if (request.url === '/') {
    response.end(JSON.stringify(data))
  }
})
server.listen(4000, () => {
  console.log('The server is running at http://localhost:4000')
})

结果:

上述代码经过两次跨域,值得注意的是浏览器向代理服务器发送请求,也遵循同源策略,最后在index.html文件打印出{"title":"fontend","password":"123456"}

4. Ajax

核心对象,ajax的XMLHttpRequest

1. var xhr=null;  
2. if (window.XMLHttpRequest)  
3.   { 
        // 兼容 IE7+, Firefox, Chrome, Opera, Safari 
4.   xhr=new XMLHttpRequest();  
5.   } else{ 
        // 兼容 IE6, IE5 
6.     xhr=new ActiveXObject("Microsoft.XMLHTTP");  
7.   } `
xhr.onreadystatechange=function(){ 
        
if(xhr.readyState==4&&xhr.status==200){ 
        
 do something
}
}
xhr.open(method,url,是否为异步)
如果为post方法,则需要使用setHeader去设置content-type即发送数据的格式
xhr.send

get 与 post 请求的区别:

  1. get 请求,请求的数据会附加在URL之后,一?分割URL 和传输数据 ,多个参数用&连接。URL的编码格式采用的是ASCll编码,所以所有的非ASCll需要编码后才能传输过去。

  2. 输出数据的大小: http规范中没有对url的长度进行限制,但是实际开发中,对于get,特定的浏览器和服务器对URL的长度有限制。因此,GET请求时,传输数据会受到长度的限制。

    对于post,不是url传值,长度是不受限制的。

  3. 安全性

    post的安全性比GET的高。

  4. 幂等性

    get是幂等的,而post不一定是幂等的

5.CSS选择器

5.1 通配符选择器 *

5.2 Id选择器

5.3 类选择器

5.4 元素选择器

选择器的优先级

!important > 行内样式 > ID选择器 >类、伪类、属性>元素、伪元素>继承>通配符

5.5 nth-child 与 nth-of-type

nth-child(n) 选择父元素下某种类型的第n个子元素,如果子元素匹配样式才会显示(其他元素算在内,回收其他元素的影响)

nth-type-of(n) 选择父元素下某种类型的第n个子元素

6. 从输入url到页面显示

6.1 浏览器的主要进程

浏览器是多进程的,浏览器的主要进程有:

(1)Broswer进程:相当于一个大管家。只有一个。负责页面的创建销毁,网络资源的管理、下载等,

(2)第三方插件进程:每启动一个插件就创建一个进程。

(3)GPU进程:用于3D绘制

(4)浏览器渲染进程(浏览器内核)(render进程):每个网页对应一个进程

img

6.2 第一部分 输入网址并解析

6.2.1 URL的组成

URL主要有 协议、主机、端口、路径、参训参数、锚点

6.2.2 解析URL

输入URL后,浏览器会解析出协议、主机、端口、路径等信息,并构造一个

1.浏览器发送,根据请求头的 (cache-control的优先级高于expries) 判断缓存是否过期 、是否命中了强缓存策略。若命中强缓存策略,就不需要发送http请求,直接在缓存中获取资源。如果未命中则进入下一步。

2.若没有命中强缓存,浏览器就发送HTTP请求,根据请求头last-modified和etag,判断是否命中协商缓存,如果命中,直接从缓存中获取资源。如果没有命中,就进入下一步。

3.如果前两步都没有命中,就直接从服务端获取资源。

6.2.2.1 强制缓存

对于强制缓存,服务器的相应的header 中会用两个字段来表明------Expires 和 Cache-Control

Expries的值为服务器返回数据的到期时间。的那个再次请求的时间小于返回的此时间,就直接使用缓存数据。 由于 服务端的时间 与客户端之间的时间可能有误差,就有了cache-control。cache-control的优先级高于expires

6.2.2.2 协商缓存

last-modify:上一次请求资源时,获得的该资源的最后修改时间

if-Modified-Since:浏览器再次请求服务器的时候,请求头会包含此字段,后面跟着在缓存中获得的最后修改时间。服务端收到此请求头发现有if-Modified-Since,则与被请求资源的最后修改时间进行对比,如果一致则返回304和响应报文头,浏览器只需要从缓存中获取信息即可。 从字面上看,就是说:从某个时间节点算起,是否文件被修改了

  • 如果真的被修改:那么开始传输响应一个整体,服务器返回:200 OK
  • 如果没有被修改:那么只需传输响应header,服务器返回:304 Not Modified

Etag:服务器响应请求时,通过该字段告诉浏览器当前资源在服务端生成的唯一标识符。(生成规则由服务器决定)。

If-None-Match:当服务器再次请求该数据中,浏览器的请求报文会包含此字段,后面的值在缓存中获取的标识。服务器收到此次报文发现If-None-Match就会与被请求资源的唯一标识符进行对比。

不同,说明被修改过,就响应整个资源内容,返回状态码200。

相同,说明没有被修改过,则响应header,浏览器直接从缓存中获取资源,返回状态码304

Etag的弊端:但是实际应用中由于Etag的计算是使用算法来得出的,而算法会占用服务端计算的资源,所有服务端的资源都是宝贵的,所以就很少使用Etag了。

缓存的优点:

减轻服务器的压力, (减压)

减少冗余的数据传递,节省带宽流量(节省流量哦)

加快了网页的加载速度(主要原因 用户体验好)

不同刷新的请求执行过程

浏览器地址栏中写入URL,回车
  • 浏览器发现缓存中有这个文件了,不用继续请求了,直接去缓存拿。(最快)
F5
  • F5就是告诉浏览器,别偷懒,好歹去服务器看看这个文件是否有过期了。于是浏览器就战战兢兢的发送一个请求带上If-Modify-since。
Ctrl+F5
  • 告诉浏览器,你先把你缓存中的这个文件给我删了,然后再去服务器请求个完整的资源文件下来。于是客户端就完成了强行更新的操作.

7. 垃圾回收机制

内存泄漏:用来为引用类型的堆内存,由于某种原因没有释放或者无法释放,导致程序运行速度减慢甚至导致程序崩溃。

7.1 引用计数法

记录每个对象被引用的次数,当的时候,立即进行回收。

(1)可以立即回收垃圾

(2)因为是即时回收,那么程序不会暂停去单独使用很长时间的GC,那么最大暂停时间也很短。

(3)不用去遍历堆里面所有的活动对象和非活动对象

(1)不能解决循环引用的问题

(2)计数器需要占用很大的空间

7.2 标记清除法

分为两个阶段:

标记阶段:对每个活动对象进行标记

清除阶段:将为被标记的对象(即非活动对象)进行清除

GC从全局出发,沿着作用域逐层向里边(深度遍历),当遍历到堆中的对象的时候就将它打上标记,直到遍历到最后一个。

遍历整个堆内存,将为未被标记的对象进行清除。

  1. 解决了循环引用的问题
  2. 实现比较简单,一个对象只有标记和未被标记两种状态。

1.会造成碎片化

2.再分配时便利次数多

其他回收算法:复制算法

8.

8.1 webpack的作用:

(1)模块打包: 可以将不同模块的文件进行打包整合到一起

(2)编译兼容

(3)能力扩展:通过webpack的Plugins机制,我们在实现模块打包和编译兼容的基础上,可以进一步实现诸如按需加载,代码压缩等一系列功能。对其他资源进行编译的预处理工作。

8.2 loader 与 Plugin 的区别

loader:loader的本质是一个函数,对接收到的内容进行转换进行转换。因为webpack只认识Javascript,loader就像是一个翻译官。

plugin:就是插件,基于事件流框架Tapable,对webpack功能的扩展,在webpack运行的生命周期中,会广播出许多事件,Plugin可以监听这些事件,在合适的机会通过webpack提供的Api改变输出结构。

8.3 使用过那些可以提高开发效率的插件?

1.webpack-merge 提取公共资源,减少代码的重复使用。

2.size-plugin 监控资源体积的变化。

8.4 一些loader 与plugin

file-loader 一般与file-loader搭配使用,功能与file类似,如果文件小于限制的大小,则会返回base64编码,否则就使用file-loader将文件移动到输出的项目中。

使用

const {CleanWebpackPlugin} = require(‘clean-webpack-plugin’)

在使用这个plugin时,要使用对象结构的方式去接受

9. 事件委托

9.1 事件委托是什么?

事件委托: 将原本需要绑定子元素上的响应事件,绑定到父元素或者更外层的元素。

9.2 事件委托的过程

(1)事件捕获阶段:从window对象传到目标节点上(上层传到底层)称为“捕获阶段”,捕获阶段不会响应任何事件

(2)处于目标阶段:在目标节点上触发,称为“目标阶段”

(3)事件冒泡阶段:从目标节点传回window对象(底层传回上层),称为 冒泡阶段,

9.3 事件委托的优点

(1)可以大量节省内存占用,减少事件注册,

(2)对于新增的子对象无需再次对其绑定。

9.4 阻止默认 和 取消冒泡

(1)event.preventDefault()

(2)event.stopParpogation()

10. diff算法

10.1 当数据发生改变的时候,vue是怎么更新节点的?

首先,根据真实的Dom生成一个 Virtual DOM,当 virtual DOM 某个节点发生改变的时候,会生成一个Vnode。 然后将Vnode 和 OldVnode的元素进行对比, 发现有不一样的地方就直接在真实的DOM上修改,然后将oldVnode的值 为Vnode。

diff的过程:失调用名为patch 的函数,比较新旧节点,边比较边给真实的DOM打补丁。

10.2 virtual DOM 与真实 DOM之间的区别?

虚拟DOM 是将真实Dom的数据抽出来 而生成的一个对象。

10.3 diff的比较方法?

在采取diff算法比较新旧节点的时候,比较只会在同层之间进行,不会进行过跨级比较。

10.4 diff流程图

当数据发生改变的时候,通过发布者通知,然后调用patch函数。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3VYIKuWG-1639491671630)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210619152140957.png)]

11. 深浅拷贝(都是相对于对象来说)

创建一个新的对象,这个对象有着原始对象属性值的一份精确拷贝。 如果属性是基本类型,拷贝的就是 基本类型的值。 如果是引用类型,拷贝的就是内存地址,所以如果其中的一个类型改变就会影响另一对象。

将一个对象从内存中,完整的拷贝一份出来,在读内存中重新开辟一个新的区域存放新对象,且修改新对象不会影响原对象

1.1.2 深拷贝的实现

方法一:

Json.parse(JSON.stringify())

方法二:

function clone1(target){

 if(typeof target ==='object'){

  let targetClone ={}

  for(var key in target){

   // 如果为引用类型再次调用该函数

   targetClone[key] = clone1(target[key])

  }

  return targetClone

 }else{

  return target //如果为基本类型 直接返回

 }

}

js

12. CSS中的单位

像素,相对长度单位,像素px 是相对于屏幕显示器的分辨率而言。

相对与当前对象文本的字体大小,如果未设置,则相对于浏览器的默认字体尺寸。

特点: 1.em不是固定的 2.em会继承父级元素的字体大小。

**rem:**相对于根元素的大小,

**%:**一般是相对于父元素来说的

**vw:**vw 视窗宽度 1vw 等于视窗的1%

**vh:**视窗高度

**vm:**css3新单位,相对于视口的宽度或高度中较小的那个。其中最小的那个被均分为100单位的vm

举个:浏览器高度900px,宽度1200px,取最小的浏览器高度,1 vm = 900px/100 = 9 px。

13. cookie ,sessionStorage、lacalStorage

三者的异同:

13.1 生命周期:

cookie:,没有设置的话,后失效

localStorage:除非被,否则将会永久保存。

sessionStorage: 仅在会话下有效,关闭页面或浏览器后就会被清除。

13.2 存放数据的大小:

cookie:左右

localStorage和sessionStorage:可以保存的信息。

13.3 http请求:

cookie:每次都会在HTTP头中,如果使用cookie保存过多数据会带来性能问题

localStorage和sessionStorage:仅在(即浏览器)中保存,不参与和服务器的通信

13.4 应用场景:

localStorage和sessionStorage唯一的差别一个是永久保存在浏览器里面,一个是关闭网页就清除了信息。localStorage可以用来传递参数,sessionStorage用来保存一些,防止用户刷新页面之后丢失了一些参数。

14. js的类型判断

方法1: typeof

一般只用来判断基本的数据类型,但是NUll除外,typeof Null 的类型为object

typeof(undefined); // undefined typeof(null); // object typeof(true); // boolean typeof(1); // number typeof(''); // string typeof(Symbol(1)); // symbol typeof(function () {}); // function typeof([]); // object typeof({}); // object typeof(new Date()); // object typeof(/abc/ig); // object typeof(Math); // object typeof(new Error('error')); // object

方法2:intenceof

instenceof 用来判断对象类型,因为内部机制是通过判断对象的原型链中是不是能找到类型的 prototype。但是,并不适用于一些基本数据类型。

1 instanceof Number; // false var num = new Number(1); num instanceof Number; // true

方法3:Object.prototype.toString.call(xxx)

15. 伪数组转化为数组

方式1:[].slice.call(arr)

方法2: Array.from() es6新增

16. position属性

16.1 static

static是position的默认属性,元素会按正常的文档流进行排序,

16.2 relative

relative会较正常文档流中的位置进行偏移,受top,bottom,left,right的影响。就是元素还在文档流中像static一样占着位置,但视觉上会有偏移,多用于absolute绝对定位的父元素。

16.3 absolute

absolute绝对定位会,相对于最近的进行过定位的**(非static)父级元素浏览器窗口**定位

16.4 fixed

fixed固定定位同样是脱离正常的文档流,一直相对于浏览器窗口定位,无论页面如何滚动,此元素总在屏幕的同一个位置。

16.5 sticky

sticky粘性定位需要指定 top,right,bottom,left 才会生效。

阈值为此元素在可视区域中与边界的距离,跨越特定阈值前为相对定位(relative),当页面滚动导致此元素跨越阈值则变为固定定位(absolute)。

17. Promise的状态

promise可能有三种状态:等待(pending)、已完成(fulfilled)、已拒绝(rejected)

特点:

不能逆向转换

then接受两个参数,一个是成功时的回调,一个是失败时的回调。

18. 伪元素与伪类的区别总结

根本区别在于:

伪类:用于向某些选择器添加特殊的效果

伪元素:用于将特殊的效果添加到某些选择器

伪类的效果可以通过添加实际的类来实现 伪元素的效果可以通过添加实际的元素来实现

19. 函数柯里化

就是讲一个可以接受多个参数的函数转化为可以接受单个参数的函数,并且返回接收余下参数且返回结果的新技术。


function curry2(fn, args) { 
        
  var len = fn.length

  var args = args || []
  // var args = Array.from(arguments).shift()

  return function () { 
        
    let newArgs = args.concat(Array.from(arguments))
// 如果传入参数长度小于len,就在此执行该函数
if (newArgs.length < len) { 
        
     return curry2.call(this, fn, newArgs)
     }
else { 
        
     // 如果长度不小于就执行
     return fn.apply(this, newArgs)
     }
  }
}

20. 不改变原数组的方法

filter()concat()slice()

21. JS中的继承

21.1 原型链继承

原理:将子类函数的原型指向父类原型的实例

缺点:

1.子类不能给父类传参

2.子类实例共用父类中引用的属性

代码:

function Parent() { 
        
    this.age = 19 this.colors = ['red', 'blue', 'black']
}
Parent.prototype.sayName = function () { 
        
    console.log('我是' + this.age)
}

function Child(friend) { 
        
    this.friend = friend
}
Child.prototype = new Parent() 
let c1 = new Child('xss') 
c1.age = 20 
c1.colors.push('pink') 
let c2 = new Child('xcrf') 
console.log(c2.colors) //[ 'red', 'blue', 'black', 'pink' ]
console.log(c1.age,c2.age)  //20 19c1.sayName() //我是20c2.sayName() //我是19

21.2 盗用构造函数继承

原理:利用call或者apply 可以改变父函数this的指向

缺点:只能在构造函数中定义函数,不能使用 父函数原型中的属性

优点:可以传参

代码:

function Parent(like) { 
        
    this.like = like 
    this.age = 19 
    this.colors = ['red', 'blue', 'black']
}
Parent.prototype.sayName = function () { 
        
    console.log('我是' + this.age)
}

function Child(friend, like) { 
        
    Parent.call(this, like) 
    this.friend = friend
}
let c1 = new Child('箭头函数', 'study') 
c1.colors.push('浏览器进程') 
// c1.sayName() //c1.sayName is not a function 不能访问
let c2 = new Child('GPU进程','play') 
console.log(c1.like,c2.like)//study play 表示传参成功console.log(c1.colors) 
//[ 'red', 'blue', 'black', '浏览器进程' ]
console.log(c2.colors)  //[ 'red', 'blue', 'black' ]

21.3 组合式继承

原理:结合了原型链继承 和 构造函数继承

优点:两者的优点

缺点:

代码:

function Parent(like) { 
        
    this.like = like 
    this.age = 19 
    this.colors = ['red', 'blue', 'black']
}
Parent.prototype.sayName = function () { 
        
    console.log('我是' + this.age)
}

function Child(friend, like) { 
        
    Parent.call(this, like) 
    this.friend = friend
}
Child.prototype = new Parent()

21.4 原型式继承

原理:在已有一个实例对象的前提下,在函数中声明一个函数,使函数的原型指向被传入的参数。返回函数的的实例

缺点:子类会共用被传入对象中的引用类型。

代码:

function inhert(o) { 
        
    function fun() { 
        }
    fun.prototype = o
    return new fun()
}
let parent = { 
        
    name: 'ty',
    age: 18,
    colors: ['red', 'black']
}
let c1 = inhert(parent) 
c1.age = 10 
c1.colors.pop() 
let c2 = inhert(parent) 
console.log(c1.age, c2.age) 
//10 18
console.log(c2.colors,c2.colors)
 //[ 'red' ] [ 'red' ] 共享一个引用类型

或者直接使用 Object.create() 来设置原型,可以看出跟上边的结果是一样的

let parent = { 
        
    name: 'ty',
    age: 18,
    colors: ['red', 'black']
}
let c1 = Object.create(parent) 
c1.age = 10 
c1.colors.pop() 

let c2 = Object.create(parent) 
console.log(c1.age, c2.age) //10 18
console.log(c2.colors,c2.colors) //[ 'red' ] [ 'red' ] 共享一个引用类型

21.5 寄生式继承

原理:根据已有的对象实例,深拷贝一个新的对象,并且可以在这个对象上添加方法和属性

缺点:每次调用继承的方法都会在函数中重新添加属性

优点:不需要共用引用的属性

function createAnthor(obj) { 
        
    let clone = JSON.parse(JSON.stringify(obj))
    clone.sayName = function () { 
        
        console.log('我是' + this.name)
    }
    return clone
}
let parent = { 
        
    name: 'ty',
    age: 18,
    colors: ['red', 'black']
}
let c1 = createAnthor(parent)
c1.colors.pop()
c1.age = 10
c1.name = 'k'
let c2 = createAnthor(parent)
c1.sayName() //我是kc2.sayName() //我是ty
console.log(c1.colors, c2.colors) //[ 'red' ] [ 'red', 'black' ]
console.log(c1.age,c2.age) //[ 'red' ] [ 'red', 'black' ]

21.6 寄生组合式继承

原因:使用组合式继承的时候,父类函数会被调用两次,在Child.prototype中会有父类函数的属性,在子类实例中中也会有父类的属性。总共有两次,

原理:克隆父类构造函数的prototype,利用寄生的方式

function Parent(like) { 
        
    this.like = like
    this.age = 19
    this.colors = ['red', 'blue', 'black']
}
Parent.prototype.sayName = function () { 
        
    console.log('我是' + this.age)
}

function Child(
        标签: hf520变送器特点

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台