这些前端问题你都会吗?

1,886 阅读8分钟

Javascript基础

1. 箭头函数和普通函数的差异

1.1 不能通过 new 关键字调用, 没有原型

  • 不能使用 new,箭头函数没有 Construct 方法,不能被用作构造函数
  • 不能通过 new 关键字调用函数,没有构建原型的需求,所以箭头函数内部也不会存在 prototype 属性,所以箭头函数没有原型
  • ES6 新增的 new.target 也没有,自然而然也不能通过 super 来访问原型属性
    // 箭头函数和普通函数的区别示例
    var Foo = () => {};
    console.log(Foo.prototype); // undefined
    

1.2 不可以改变 this 绑定,由外层非箭头函数决定,所以使用 call, apply, bind 也不会影响

  • function 函数(优先级按序号从高到低)
    1. 在调用函数时使用 new 关键字,函数内的 this 是一个全新的对象。
    2. 如果 apply、call 或 bind 方法用于调用、创建一个函数,函数内的 this 就是作为参数传入这些方法的对象。
    3. 当函数作为对象里的方法被调用时,函数内的 this 是调用该函数的对象。
    4. 如果调用函数不符合上述规则,那么 this 的值指向全局对象(global object)。
    5. 浏览器环境下 this 的值指向 window 对象,但是在严格模式下'use strict',this 的值为 undefined。
  • 箭头函数
    • 无论如何执行或在何处执行,箭头函数内部的 this 值始终等于离它最近的外部函数的 this 值。换句话说,箭头函数可按词法解析 this,箭头函数没有定义自己的执行上下文。
    • 熟悉 React 的同学都知道,之前如果在类里面使用方法我们需要先 bind 一下 this, 但箭头函数都给我们解决了
    const obj = { value: 2, callback: function(arr) { var self = this // 在嵌套函数里面使用外面的 this 需要先定义变量的情况 function showThis() { console.log(self.value) } showThis() } } const obj2 = { value: 2, callback: function(arr) { showThis = () => { console.log(this.value) } showThis() } } obj.callback() // 2 obj2.callback() // 2

1.3 不支持 arguments,所以根据作用域链,会拿到外层函数的 arguments

  • 常规函数
    • 有一个arguments 的类数组对象,包含了函数的所有参数
    • 经常使用在我们没有具体定义参数的情况下
  • 箭头函数
    • 箭头函数是不支持arguments 的类数组对象
    • 如果你在箭头函数里面使用,按照作用域链的查找规则,会一直查询到最近的非箭头函数,然后实际使用的是非箭头函数里面的 arguments
    • 为此,ES6 也提供了这种不定参数情况下的参数的操作,看下面的例子
    // ...args 会把不定参数转换成一个数组,
    // 但使用这个也要注意,只能使用一次且放在所有参数末尾
    const add = (...args) => {
         return args.reduce((cur, i) => cur + i, 0)
    }
    add(5, 10, 15) // 30
    

1.4 不支持重复命名参数

  • 无论在严格还是非严格模式下,箭头函数都不支持重复的命名参数,而传统函数只有在严格模式下才不支持重复的命名参数。
var a = (b, b)=>{
    b = 100
    console.log(this)
    console.log(b)
} // Uncaught SyntaxError: Duplicate parameter name not allowed in this context

1.5 隐式返回

  1. 函数内部一直都有一个隐藏的返回值,使用 function 如果什么都不定义,其实默认就会返回 undefined,我理解这是终止函数的执行,用代码来表示
function noReturn() {
  console.log('aaa')
  return // 这个不需要手动写,不写默认就是
  // 如果你在函数中没有需要 return 的,就会自动给你加上一个
}
  1. 箭头函数省了一个 return,适合这种函数体内只有一行代码的情况,多行还是需要写的
    • 如果 return 的是一个值,不需要 {}
    • 如果返回的是一个字面量, 需要加 ()
      const add = (a, b) => a + b
      const obj = () => ({ a: 1 })
      

2. 数组方法整理

方法名对应版本功能原数组是否改变
concat()ES5-合并数组,并返回合并之后的数据n
join()ES5-使用分隔符,将数组转为字符串并返回n
pop()ES5-删除最后一位,并返回删除的数据y
shift()ES5-删除第一位,并返回删除的数据y
unshift()ES5-在第一位新增一或多个数据,返回长度y
push()ES5-在最后一位新增一或多个数据,返回长度y
reverse()ES5-反转数组,返回结果y
slice()ES5-截取指定位置的数组,并返回n
sort()ES5-排序(字符规则),返回结果y
splice()ES5-删除指定位置,并替换,返回删除的数据y
toString()ES5-直接转为字符串,并返回n
valueOf()ES5-返回数组对象的原始值n
indexOf()ES5查询并返回数据的索引n
lastIndexOf()ES5反向查询并返回数据的索引n
forEach()ES5参数为回调函数,会遍历数组所有的项,回调函数接受三个参数,分别为value,index,self;forEach没有返回值n
map()ES5同forEach,同时回调函数返回数据,组成新数组由map返回n
filter()ES5同forEach,同时回调函数返回布尔值,为true的数据组成新数组由filter返回n
every()ES5同forEach,同时回调函数返回布尔值,全部为true,由every返回truen
some()ES5同forEach,同时回调函数返回布尔值,只要由一个为true,由some返回truen
reduce()ES5归并,同forEach,迭代数组的所有项,并构建一个最终值,由reduce返回n
reduceRight()ES5反向归并,同forEach,迭代数组的所有项,并构建一个最终值,由reduceRight返回n

3. 数组浅拷贝方法

序号方式例子说明
1concatlet res = arr.concat();连接数组返回一个新数组
2slicelet res = arr.slice();返回选定的元素默认从0开始到结尾
3解构let res = [...arr];创建了一个新的数组赋值为当前数组的值
4解构2let [...res] = arr;同上
5maplet res = arr.map(i=>i);遍历数组逐个返回元素给res
6Array.oflet res = Array.of(...arr);该方式将一堆数字转化成数组返回

2.Webpack

2.1 webpack 性能优化-构建速度

1. 优化babel-loader

module: {
	rules: [
        {
          test: /\.js$/,
          use: ['babel-loader?cacheDirectory'], // 开启缓存
          include: path.resolve(__dirname, 'src') // 明确范围
          // // 排除范围,include 和 exclude 两者选一个即可
          // exclude: path.resolve(__direname, 'node_modeles')
      }
    ]
}

2. IgnorePlugin 忽略无用文件

  • 用IngorePlugin忽略无用文件 例如:moment会支持多语言,如何只引入中文模块?
// 忽略 moment 下的 /locale目录
new webpack.IgnorePlugin(/\.\/locale/, /moment/)

// 业务代码中动态引入语言包
import 'moment/locale/zh-cn'

3. noParse 避免重复打包

  • noParse避免重复打包
module: {
  noParse: [/react\.min\.js$/]
}
  • IgnorePlugin、noParse区别:
    • IgnorePlugin直接不引入,代码中没有
    • noParse(类似vue.min.js已经模块化处理过)引入,但不打包

4. happypack 多进程打包工具

  • JS 单线程,开启多进程打包
  • 提高构建速度(特别是多核CPU)
    const HappyPack = require('happypack')
    
    // 步骤1 module.rules下-将对.js的文件交给id为label的HappyPack实例
    {
      test: /\.js$/,
      use: ['happypack/loader?id=babel']
    }
    
    // 步骤2 happyPack开启多进程打包
    new HappyPack({
     // 用唯一的标识符 id 来代表当前的HappyPack 是用来处理一类特定的文件
     id: 'babel',
     // 如何处理.js 文件,用法和Loader 配置一样
     loaders: ['babel-loader?cacheDirectory']
    })

5. ParalleUglifyPlugin 多进程代码压缩

  • webpack 内置 Uglify 工具压缩JS
  • JS单线程,开启多进程压缩更快
  • 和happyPack 同理,多进程
    // 使用 ParallelUglifyPlugin 并行压缩输出的 JS 代码
    const ParallelUglifyPlugin = require('ParallelUglifyPlugin')
    
    new ParallelUglifyPlugin({
      // 传递给 UglifyJS 的参数
      // (还是使用 UglifyJS 压缩,只不过帮助开启了多进程)
      uglifyJS: {
        output: {
          beautify: false, // 最紧凑的输出
          comments: false // 删除所有的注释
        },
        compress: {
            // 删除所有的 `console` 语句,可以兼容ie浏览器
            drop_console: true,
            // 内嵌定义了但是只用到一次的变量
            collapse_vars: true,
            // 提取出出现多次但是没有定义成变量去引用的静态值
            reduce_vars: true
         }
      }
    })

6.自动刷新

  • 自动刷新:整个网页都刷新,速度较慢
  • 自动刷新:状态会丢失(表单内数据会丢失)

7. 热更新

  • 热更新:新代码生效,网页不刷新,状态不丢失
  • HotModuleReplacementPlugin插件在webpack/lib下
// 配置 在entry中的index中
 index: [
    'webpack-dev-server/client?http://localhost:8080/',
    'webpack/hot/dev-server',
    path.join(srcPath, 'index.js')
 ]
 
 // 配置2
在plugins中new HotModuleReplacementPlugin()

// 配置3
在devServer加hot: true

// 配置4
业务中加module.hot来确定哪些范围需要触发热更新

8. DllPlugin 动态链接库插件

  • 使用原因
    1. 前端框架Vue、React 体积大 构建慢
    2. 较稳定,不常升级版本
    3. 同一个版本只构建一次即可,不用每次都重新构建
  • webpack已经内置DllPlugin支持
    • DllPlugin - 打包出dll文件
    • DllReferencePlugin - 使用dll文件
  • 使用方式
// 新建webpack.dll.js 文件
const path = require('path')
const DllPlugin = require('webpack/lib/DllPlugin')
const { srcPath, distPath } = require('./paths')

module.exports = {
  mode: 'development',
  // JS 执行入口文件
  entry: {
    // 把 React 相关模块的放到一个单独的动态链接库
    react: ['react', 'react-dom']
  },
  output: {
    // 输出的动态链接库的文件名称,[name] 代表当前动态链接库的名称,
    // 也就是 entry 中配置的 react 和 polyfill
    filename: '[name].dll.js',
    // 输出的文件都放到 dist 目录下
    path: distPath,
    // 存放动态链接库的全局变量名称,例如对应 react 来说就是 _dll_react
    // 之所以在前面加上 _dll_ 是为了防止全局变量冲突
    library: '_dll_[name]',
  },
  plugins: [
    // 接入 DllPlugin
    new DllPlugin({
      // 动态链接库的全局变量名称,需要和 output.library 中保持一致
      // 该字段的值也就是输出的 manifest.json 文件 中 name 字段的值
      // 例如 react.manifest.json 中就有 "name": "_dll_react"
      name: '_dll_[name]',
      // 描述动态链接库的 manifest.json(索引文件) 文件输出时的文件名称
      path: path.join(distPath, '[name].manifest.json'),
    }),
  ],
}
  • DllReferencePlugin(记得 rules 中忽略打包的node_modules这个dll 代码)
 	plugins: [
        new webpack.DefinePlugin({
            // window.ENV = 'production'
            ENV: JSON.stringify('development')
        }),
        // 第三,告诉 Webpack 使用了哪些动态链接库
        new DllReferencePlugin({
            // 描述 react 动态链接库的文件内容
            manifest: require(path.join(distPath, 'react.manifest.json')),
        }),
    ],

2.2 webpack性能优化-产出代码

作用

  1. 打包出的体积更小
  2. 合理分包,不重复加载
  3. 速度更快,内存使用更少

优化方式

  1. 小图片base64编码(对图片使url-loader配置options.limit)
  2. bundle加hash值([name].[contentHash:8].js)
    • contentHash:8 是根据内容算出的 8位hash值
  3. 懒加载()=>import('')
  4. 提取公共代码splitChunks:公共代码和三方代码
  5. IgnorePlugin: 忽略文件不引入打包
  6. 用cdn加速:
    • 在output中加publicPath
    • 在url-loader中的options中也加入publicPath , 设置图片
  7. 使用production
  8. Scope Hosting
    • 改变打包作用域,使占用更新写,打包更快些
  9. 开启 gzip