面试题

// 题目0:需求: 使用ES6 的Proxy实现数组负索引。 
(负索引:例如,可以简单地使用arr[-1]替代arr[arr.length-1]访问最后一个元素,[-2]访问倒数第二个元素,以此类推)
const targetArr = (sourceArr) => {
    return new Proxy(sourceArr, {
        get(target, key, receiver) {
            // 数组
            if (Array.isArray(sourceArr)) {
                const len = target.length
                const intKey = Number(key)
                return Reflect.get(target, intKey < 0 ? len + intKey : intKey, receiver)
            }
            // 对象
            return Reflect.get(target, key, receiver)
        }
    })
}

const test = targetArr([1, 2, 3, 4, 5])
console.log(test[-1])
console.log(test[-2])

const obj = targetArr({'-1': 'chen', '-2': 'feng' })
console.log(obj[-1])
console.log(obj[-2])

//  题目1、使用ES6 的Proxy实现数组负索引。
// (负索引:例如,可以简单地使用arr[-1]替代arr[arr.length-1]访问最后一个元素,[-2]访问倒数第二个元素,以此类推)
const targetArr = (sourceArr) => {
  return new Proxy(sourceArr, {
    get(target, key, receiver) {
      // 数组
      if (Array.isArray(sourceArr)) {
        const len = target.length;
        const intKey = Number(key);
        return Reflect.get(
          target,
          intKey < 0 ? len + intKey : intKey,
          receiver
        );
      }
      // 对象
      return Reflect.get(target, key, receiver);
    },
  });
};

const test = targetArr([1, 2, 3, 4, 5]);
console.log(test[-1]);
console.log(test[-2]);

const obj = targetArr({ "-1": "chen", "-2": "feng" });
console.log(obj[-1]);
console.log(obj[-2]);

// 题目2、宏任务、微任务
// console.log(1);
// setTimeout(() => {
//     console.log(2);
//     process.nextTick(() => {
//         console.log(3);
//     });
//     new Promise((resolve) => {
//         console.log(4);
//         resolve();
//     }).then(() => {
//         console.log(5);
//     });
// });
// new Promise((resolve) => {
//     console.log(7);
//     resolve();
// }).then(() => {
//     console.log(8);
// });
// process.nextTick(() => {
//     console.log(6);
// });
// setTimeout(() => {
//     console.log(9);
//     process.nextTick(() => {
//         console.log(10);
//     });
//     new Promise((resolve) => {
//         console.log(11);
//         resolve();
//     }).then(() => {
//         console.log(12);
//     });
// });

// 1 7 6 8 2 4 3 5 9 11 10 12
console.log("======================");

// 题目3、arguments参数
function side(arr) {
  arr[0] = arr[2];
}
function a(a, b, c = 3) {
  c = 10;
  side(arguments); // 1 1 10
  return a + b + c; // 12
}

console.log("a(1, 1, 1): ", a(1, 1, 1)); // 12
// 加了默认值,则转为严格模式(其实也可以使用 "use strict" 转),这时候参数(a、b、c)与 arguments 没有绑定关系,所以修改 arguments 不会影响到参数(a、b、c)的值,修改参数(a、b、c)也不会影响到 arguments。
// 不加默认值,则为非严格模式,结果和上面的相反。
console.log("=======================");

// 题目4
var min = Math.min(); //
max = Math.max();
console.log(min < max);
// Math.min 的参数是 0 个或者多个,如果多个参数很容易理解,返回参数中最小的。如果没有参数,则返回 Infinity,无穷大。
// 而 Math.max 没有传递参数时返回的是-Infinity.所以输出 false
console.log("=======================");

// 题目5
var a1 = [0];
if (a1) {
  console.log("a1: ", !!a1 === true); // 执行
} else {
  console.log("a1=", a1);
}
console.log("=======================");

// 题目6
var x = 1;
if (function f() {}) {
  x += typeof f;
}
console.log(x); // 1undefined
// 条件判断为假的情况有:0,false,'',null,undefined,未定义对象。
// 函数声明写在运算符中,其为true,但放在运算符中的函数声明在执行阶段是找不到的。另外,对未声明的变量执行typeOf不会报错,会返回undefined
console.log("=======================");

// 题目7
function f() {
  return f;
}
console.log(new f() instanceof f); // false
// a instanceof b 用于检测a是不是b的实例。如果题目f中没有return f,则答案明显为true;
// 而在本题中new f()其返回的结果为f的函数对象,其并不是f的一个实例。

// 题目8
[typeof null, null instanceof Object]; // ['object', false]
// typeof 的结果列表
//      Undefined "undefined"
//      Null "object"
//      Boolean "boolean"
//      Number "number"
//      String "string"
//      Symbol "symbol"
//      Function "function"
//      Object "object"

// 题目8
function showCase(value) {
  switch (value) {
    case "A":
      console.log("Case A");
      break;
    case "B":
      console.log("Case B");
      break;
    case undefined:
      console.log("undefined");
      break;
    default:
      console.log("Do not know!");
  }
}
showCase(new String("A")); // Do not know!
// switch 是严格比较, String 实例和 字符串不一样.
// var str1 = 'str';
// var str2 = new String('str');
// console.log(typeof str1); // "string"
// console.log(typeof str2); //"object"

// 题目9
var arr = [0, 1];
arr[5] = 5;
newArr = arr.filter(function (x) {
  return x === undefined;
});
console.log(newArr.length); // 0
// filter 为数组中的每个元素调用一次 callback 函数,并利用所有使得 callback 返回 true 或等价于 true 的值的元素创建一个新数组。
// callback 只会在已经赋值的索引上被调用,对于那些已经被删除或者从未被赋值的索引不会被调用。那些没有通过 callback 测试的元素会被跳过,不会被包含在新数组中。
// 也就是说 从 2-4 都是没有初始化的'坑'!, 这些索引并不存在与数组中. 在 array 的函数调用的时候是会跳过这些'坑'的.

// 题目13
function user(obj) {
  // obj传入的是引用
  obj.name = "京程一灯"; // 修改引用的值
  obj = new Object(); // obj 指向了一个新地址
  obj.name = "精英班"; //  修改的是新对象,没法改变传入的对象
}
let person = new Object();
user(person);
console.log(person.name); // 京程一灯
console.log("==========================");

// 题目14
// let x, y;
// try {
//   throw new Error();
// } catch (x) {
//   x = 1;
//   y = 2;
//   console.log(x);
// }
// console.log(x);
// console.log(y);
// 1
// undefined
// 2
console.log("15==========================");

// 题目15
let length = 10;
function fn() {
  console.log(this.length);
}
var obj2 = {
  length: 5,
  method: function (fn) {
    fn();
    arguments[0]();
  },
};
obj2.method(fn, 1);
// 0 2
// window对象原先上有length属性,所以输出的是原先的值0
// arguments[0]指向 fn,所以 arguments[0]() 是作为 arguments对象的属性[0]来调用 fn的,所以 fn 中的 this 指向属性访问主体的对象 arguments;
// 这里this指向arguments.因为fn作为一个参数存储在arg对象里,argumengts的长度为2,所以输出2

// 题目16
var a = 10;
var foo = {
  a: 20,
  bar: function () {
    var a = 30;
    return this.a;
  },
};
console.log(foo.bar());
console.log(foo.bar());
console.log((foo.bar = foo.bar)());
console.log((foo.bar, foo.bar)());
// 20 20 10 10
// 1)第一问 foo.bar(), foo调用,this指向foo , 此时的 this 指的是foo,输出20
// 2) 第二问 (foo.bar)(), 给表达式加了括号,而括号的作用是改变表达式的运算顺序,而在这里加与不加括号并无影响;相当于foo.bar(),输出20
// 3)第三问 (foo.bar=foo.bar)(), 所以这里的this指代的是window,输出104)
// 4) 第四问 (foo.bar,foo.bar)(), 逗号表达式,求解过程是:先计算表达式1的值,再计算表达式2的值,……一直计算到表达式n的值。最后整个逗号表达式的值是表达式n的值。逗号运算符的返回值是最后一个表达式的值。
//    经过赋值,运算符运算后,都是纯粹的函数,不是对象方法的引用。所以函数指向的this都是windows的。

// 题目17
const value = { number: 10 };
const multiply = (x = { ...value }) => {
  console.log((x.number *= 2));
};
multiply();
multiply();
multiply(value);
multiply(value);
// 我们前两次调用 multiply 函数且不传递值,那么每一次 x 的默认值都为 {number:10} ,因此打印出该数字的乘积值为 20。
// 第三次调用 multiply 时,我们传递了一个参数,即对象 value。*=运算符实际上是 x.number=x.number*2的简写,我们修改了 x.number的值,并打印出值 20。
// 第四次,我们再次传递 value对象。x.number之前被修改为 20,所以 x.number*=2打印为 40。
console.log("17=========================");

// 题目18
// 能否以某种方式为下面的语句使用展开运算而不导致类型错误
var obj3 = { x: 1, y: 2, z: 3 };
// [...obj]; // TypeError
// 能否以某种方式为上面的语句使用展开运算而不导致类型错误
// 如果可以,写出解决方式
obj3[Symbol.iterator] = function* () {
  const values = Object.values(this);
  for (const value of values) {
    yield value;
  }
};
console.log("18=====================");

// 题目19: 完成一个safeGet函数,可以安全的获取无限多层次的数据
var data = { a: { b: { c: "yideng" } } };
// safeGet(data, 'a.b.c') // => yideng
// safeGet(data, 'a.b.c.d') // => undefined
// safeGet(data, 'a.b.c.d.e.f.g') // => undefined
var safeGet = (data, path) => {
  try {
    return path.split(".").reduce((data, k) => data[k], data);
  } catch (error) {
    return undefined;
  }
};
console.log("a.b.c ", safeGet(data, "a.b.c")); // yideng
console.log(" a.b.c.d", safeGet(data, "a.b.c.d")); // => undefined
console.log("a.b.c.d.e.f.g", safeGet(data, "a.b.c.d.e.f.g")); // => undefined
console.log("19=====================");

// 题目20: 数组sort
const arr1 = ["a", "b", "c"];
const arr2 = ["b", "c", "a"];
console.log(
  arr1.sort() === arr1,
  arr2.sort() === arr2,
  arr1.sort() === arr2.sort()
);
// true true false
console.log("20=====================");

// 题目21
const arrLike = {
  length: 4,
  0: 0,
  1: 1,
  "-1": 2,
  3: 3,
  4: 4,
};
console.log(Array.from(arrLike)); // [0, 1, undefined, 3]
console.log(Array.prototype.slice.call(arrLike)); // [0, 1, 空白, 3]
// Array.from(伪数组对象, 可迭代对象),
// Array.prototype.slice.call(伪数组对象)能把类数组对象转化成数组,arrLike规定了长度4的数组。

// 题目22
console.log(1 < 2 < 3); // true
console.log(3 > 2 > 1); // false

// 题目23
// Vue父组件可以监听到子组件的生命周期吗?如果能请写出你的实现方法。
// 可以

// 1)、实现方式一
// 比如有父组件 Parent 和子组件 Child,如果父组件监听到子组件挂载 mounted 就做一些逻辑处理,可以通过以下写法实现:
// // Parent.vue
// 
// // Child.vue
// mounted() {
//   this.$emit("mounted");
// }

// 2)、实现方式二
//  Parent.vue
{
  /* 
doSomething() {
   console.log('父组件监听到 mounted 钩子函数 ...');
}, */
}
//  Child.vue
// mounted(){
//    console.log('子组件触发 mounted 钩子函数 ...');
// },
// 以上输出顺序为:
// 子组件触发 mounted 钩子函数 ...
// 父组件监听到 mounted 钩子函数 ...
console.log("23================================");

// 题目23
// 请写一个函数,输出出多级嵌套结构的 Object 的所有 key 值
var objData = {
  a: "12",
  b: "23",
  first: {
    c: "34",
    d: "45",
    second: { 3: "56", f: "67", three: { g: "78", h: "89", i: "90" } },
  },
};
// => [a,b,c,d,e,f,g,h,i]
function getObjectAllKey(obj, keys = []) {
  for (const key in obj) {
    if (obj[key] instanceof Object) {
      getObjectAllKey(obj[key], keys);
    } else {
      keys.push(key);
    }
  }
  return keys;
}
console.log("getAllKey: ", getObjectAllKey(objData));
console.log("24================================");

// 题目25
// 用尽量短的代码实现一个 arrary 的链式操作,将数组中的大于 10 的值进行一个累加
console.log(
  "25:",
  [9, 11, 12, 13].filter((v) => v > 10).reduce((pre, val) => pre + val, 0)
);

// 题目26
// 通过 link 进来的 css 会阻塞页面渲染吗,Js 会阻塞吗,如果会如何解决?
// 答:
//    DOM解析和CSS解析是两个并行的进程,所以这也解释了为什么CSS加载不会阻塞DOM的解析。
//    然而,由于Render Tree是依赖于DOM Tree和CSSOM Tree的,所以他必须等待到CSSOM Tree构建完成,也就是CSS资源加载完成(或者CSS资源加载失败)后,才能开始渲染。因此,CSS加载是会阻塞Dom的渲染的。
//    由于js可能会操作之前的Dom节点和css样式,因此浏览器会维持html中css和js的顺序。因此,样式表会在后面的js执行前先加载执行完毕。所以css会阻塞后面js的执行。

// 题目27
// Css 如何画出一个扇形,动手实现下
// 
// 
// 
//     
//     
//     Css画出一个扇形
//     
// 
// 
//     
// // // 题目28 // 箭头函数和普通函数的区别 // 1、箭头函数不能绑定argruments,取而代之的是rest // 2、箭头函数是匿名函数,不能作为构造函数,不能new // 3、箭头函数没有原型属性 // 4、箭头函数不能绑定this,会将离自己最近的一个普通函数的this作为自己的this // 5、call、apply、bind都无法改变箭头函数中this的指向 console.log("28================================="); // 题目29 // 将 153812.7 转化为 153,812.7 function parseMoney(num) { let [interger, decimal] = String.prototype.split.call(num, "."); interger = interger.replace(/\d{1,3}(?=(\d{3})+$)/g, "$&,"); decimal = decimal ? `.${decimal}` : ""; return interger + decimal; } console.log(parseMoney(153812.7)); // 153,812.7 console.log("29================================="); // 题目30 // [] == ![] 为什么 console.log("[] == ![]===", [] == ![]); // 按照一定的规则 // ![] 转为布尔值false,实际上比较的是[] == false // 进行比较之前,会转为Number类型,Number([]) = 0 , Number(false) = 0 // 所以 [] == ![] 成立 // [] == [] false, {} == {} false 都是引用类型 // {} == !{} false,转换为{} == false -> Number({}) = NAN console.log("30=================================="); // 题目31 // 给一个字符串比如'abca',返回第一个不重复的字母 // function fn(str){ // const map = new Map(); // for (const s of str) { // console.log('s===', s); // if(map.has(s)){ // map.delete(s); // }else { // map.set(s, 1) // } // } // console.log('map=====', map); // return Array.from(map.keys())[0]; // } // console.log('fn======', fn('abcdea')); // console.log('31=================================='); // 题目32 // 判断一个对象是否为空 const aa = { name: "a", }; const bb = {}; // 1) Object.keys() console.log("aa===", Object.keys(aa).length === 0); // false console.log("bb===", Object.keys(bb).length === 0); // true // 2) JSON.stringify() console.log("aa===", JSON.stringify(aa) === "{}"); // false console.log("bb===", JSON.stringify(bb) === "{}"); // true // 题目33 // ES6 中的 map 和原生的对象有什么区别 // object的键是字符串。 // map的键可以是任意类型。 // object获取键值使用Object.keys(返回数组); // Map获取键值使用 map变量.keys() (返回迭代器)。 // 题目34 // a 标签默认事件禁掉之后做了什么才实现了跳转 // 首先,preventDefault()会导致阻止默认行为(即链接跳转),如果需要在阻止了默认行为后,依旧能够实现页面跳转,可以使用location.href // 题目35 // function say(word) { // let word = "hello"; // console.log(word); // } // say("hello Lili"); // 报错:Identifier 'word' has already been declared // 题目36 var yideng = Array(3); yideng[0] = 2; var result = yideng.map((elem) => { return "1"; }); console.log("result======", result); // ['1', 空属性 × 2] console.log("36==============================="); // 题目36 var length1 = 10; function fn() { console.log(this.length1); } var yideng = { length1: 5, method: function (fn) { fn(); console.log("arguments", arguments); // Arguments(2) [ƒ, 1, callee: ƒ, Symbol(Symbol.iterator): ƒ] arguments[0](); }, }; yideng.method(fn, 1); // 10 2 console.log("36================================"); // 题目37 // 介绍 instanceof 原理,并手动实现 function myInstanceof(son, parent) { const prototype = parent.prototype; let proto = Object.getPrototypeOf(son); while (true) { // 说明已经找到原型链最顶层 if (proto === null) return false; // 构造函数的原型(R.prototype)存在于实例的原型链上(L.__proto__) if (proto === prototype) return true; // 递归原型链向上查找 proto = Object.getPrototypeOf(proto); } } function C() {} function D() {} const o = new C(); console.log(myInstanceof(o, C)); // true console.log(myInstanceof(o, D)); // false // 题目38 // webpack 的 loader 和 plugin 的区别,都使用过哪些 loader 和 plugin // https://github.com/lgwebdream/FE-Interview/issues/936 // ignore-plugin:忽略文件 // uglifyjs-webpack-plugin:不支持 ES6 压缩 (Webpack4 以前使用) // terser-webpack-plugin: 支持压缩 ES6 (Webpack4) // webpack-parallel-uglify-plugin: 多进程执行代码压缩,提升构建速度 // mini-css-extract-plugin: 分离样式文件,CSS 提取为独立文件,支持按需加载 // serviceworker-webpack-plugin:为网页应用增加离线缓存功能 // clean-webpack-plugin: 目录清理 // speed-measure-webpack-plugin: 可以看到每个 Loader 和 Plugin 执行耗时 // webpack内置UglifyJsPlugin,压缩和混淆代码。 // webpack内置CommonsChunkPlugin,提高打包效率,将第三方库和业务代码分开打包。 // ProvidePlugin:自动加载模块,代替require和import // html-webpack-plugin可以根据模板自动生成html代码,并自动引用css和js文件 // extract-text-webpack-plugin 将js文件中引用的样式单独抽离成css文件 // DefinePlugin 编译时配置全局变量,这对开发模式和发布模式的构建允许不同的行为非常有用。 // HotModuleReplacementPlugin 热更新 // DllPlugin和DllReferencePlugin相互配合,前者第三方包的构建,只构建业务代码,同时能解决Externals多次引用问题。DllReferencePlugin引用DllPlugin配置生成的manifest.json文件,manifest.json包含了依 // 赖模块和module id的映射关系 // optimize-css-assets-webpack-plugin 不同组件中重复的css可以快速去重 // webpack-bundle-analyzer 一个webpack的bundle文件分析工具,将bundle文件以可交互缩放的treemap的形式展示。 // compression-webpack-plugin 生产环境可采用gzip压缩JS和CSS // happypack:通过多进程模型,来加速代码构建 // 题目39 // for..of 和 for...in 是否可以直接遍历对象?为什么?如果不可以有什么解决方案? // for..of 不能直接遍历对象,for...in 可以直接遍历对象 // 1)Object.key() const obj11 = { a: 1, b: 2, c: 3 }; for (const prop of Object.keys(obj11)) { console.log(prop); } // a // b // c // 2)Object.values() for (const val of Object.values(obj11)) { console.log(val); } // 1 // 2 // 3 // 3)Object.entries() for (const item of Object.entries(obj11)) { console.log(item); } // ['a', 1] // ['b', 2] // ['c', 3] // 4)Iterator // obj11{Symbol.iterator} = function () { // let index = 0 // const _this = this // const keys = Object.keys(_this) // const len = keys.length // return { // next() { // if (index < len) { // return { // value { // value: _this[keys[index++]], // done: false // } // } // return { // value: null, // done: true // } // } // } // } // } // 5)Generator object[Symbol.iterator] = function* () { const keys = Object.keys(this); for (let i = 0, len = keys.length; i < len; i++) { yield this[keys[i]]; } }; for (const item of obj) { console.log(item); }

你可能感兴趣的:(javascript)