前端js面试题

223 阅读15分钟

牛客面试题

单选题

一、js概念与类型检测

  1. 利用typeof检测值的类型

    • typeof检测出为number:数字或者NaN;
    • typeof检测出Object:对象或者null
    • typeof null == Object ;(注意:null instanceof Object 为false,因为null本身并不是一个Object类型)
  2. BigInt 可以表示任意精度整数的基本数据类型,存储在栈中

  3. NaN是非数字,非数字是不相同的

    console.log(NaN == NaN); // false
    
  4. Symbol.for('a') 执行30次,会先检查给定的key是否已经存在,如果不存在才会新建一个值; Symbol('a')执行30次,每次都会返回不同的Symbol值

      console.log( Symbol.for('a') === Symbol.for('a') ); // true
      console.log( Symbol('a') === Symbol('a') ); // false
    
  5. || 真前假后:为真返回||前面的值,为假返回||后面的值

    typeof [] || function; // 得到的结果是Object
    console.log(1 || []); // 1 而不是true
    console.log(true || 1); // true
    console.log([] && ''); // []
    

    &&假前真后:为假返回&&前面的值,为真返回&&后面的值

  6. 利用 “==” 判断数据是否相等:

    • 如果有个操作数是布尔值,就先转为数值再比较

    • 有个操作是字符串,另一个操作数是数值,会将字符串转为数值再比较

    • undefined和null除了与自身或彼此比较,和其它任何类型比较都是不等的

      console.log(undefined == 0); // false
      console.log(undefined == false); // falseconsole.log(undefined == undefined); // true
      console.log(null == null); // true
      console.log(null == undefined); // true
      
  7. ! 逻辑非:操作非布尔值类型的数据时,会先把数据转换为布尔值再取反

    console.log(![] == false); // true
    console.log(Boolean([])); // true
    
  8. 其它

    console.log(Number('a')); // NaN
    console.log(-1 == true); // false
    console.log(3 + '2'); // 32
    

二、Math类型

  1. 小数转整数:

    • round 四舍五入
    • ceil 天花板
    • floor 地板
        console.log(Math.round(7.25)); // 7
        console.log(Math.ceil(7.25)); // 8
        console.log(Math.floor(7.25)); // 7
    
  2. Math.random()随机生成数: [0,1)

三、字符串

  1. split 将字符串分割成字符串数组,并且删除对应索引处的值

  2. match 返回匹配数组

  3. indexOf和search返回匹配子串的第一个位置

    区别:

    • indexOf查找字符串中是否有子串,顺便返回一个索引,资源消耗小,效率高
    • search 是查找有某些特征的字符串,比如查找a开头的后面跟着的数字的字符串,并返回匹配的第一个索引,资源消耗大
    console.log("123".split(2)); // ['1','3']
    console.log('123'.match('23')); // ['23', index: 1, input: '123', groups: undefined]
    console.log('123'.indexOf('23')); // 1
    console.log('123'.search('23')); // 1
    let text = "Mr. Blue has a blue house";
    console.log(text.search(/blue/i)); // 4

四、JSON数据

  1. JSON.stringify() 会输出不包含空格或者缩进的JSON字符串

    	let json1 = {
          title: "Json.stringify",
          author: [
            "浪里行舟"
          ],
          year: 2021
        };
        let jsonText1 = JSON.stringify(json1);
        console.log(jsonText1);
    	// {"title":"Json.stringify","author":["浪里行舟"],"year":2021}
    
  2. JSON.stringify() :第二个参数,过滤器

      let json2 = {
       title: "json.stringify",
       author: ["浪里行舟" ],
       year: 2021,
       like: 'frontend',
       weixin: 'frontJS'
      };
      let jsonText2 = JSON.stringify(json2, ['weixin']);
      console.log(jsonText2); // {"weixin":"frontJS"}
    
     const students = [
          {
            name: 'james',
            score: 100,
          }, {
            name: 'jordon',
            score: 60,
          }, {
            name: 'kobe',
            score: 90,
          }
        ];
    
        function replacer (key, value) {
          if (key === 'score') {
            if (value === 100) {
              return 'S';
            } else if (value >= 90) {
              return 'A';
            } else if (value >= 70) {
              return 'B';
            } else if (value >= 50) {
              return 'C';
            } else {
              return 'E';
            }
          }
          return value;
        }
        // 把分数换成等级
        console.log(JSON.stringify(students, replacer, 4))
        // 上面的4是字符串缩进:每级缩进4格
    	//     [
    	//     {
    	//         "name": "james",
    	//         "score": "S"
    	//     },
    	//     {
    	//         "name": "jordon",
    	//         "score": "C"
    	//     },
    	//     {
    	//         "name": "kobe",
    	//         "score": "A"
    	//     }
    	//    ]
    
  3. 通过JSON.stringify() 转为了 null

    • 有NaN
    • 有Infinity
    • 有undefined
    • 有function() {}
    • 有symbol("")
    console.log(JSON.stringify([NaN, Infinity]));     // [null,null]
    console.log(JSON.stringify([undefined, function () { }, Symbol("")])); 
    // '[null,null,null]'
    
     // 特殊
    console.log(JSON.stringify({ x: undefined, y: function () { }, z: Symbol("") }));    // '{}'
    
  4. 不可枚举的属性默认会被忽略

	let personObj = Object.create(null, {
      name: { value: "浪里行舟", enumerable: false },
      year: { value: "2021", enumerable: true },
    })
    console.log(JSON.stringify(personObj)) // {"year":"2021"}

五、数组

  1. shift会改变原数组,返回的是number类型;filter不会改变原数组,返回的是Object类型

     console.log(typeof(arr1.shift())); // number
     console.log(typeof(arr1.filter((item)=>item > 1))); // object
    
  2. Array.form()去重,和nums.filter()类似

    let nums = [5,1,1,2,2,3,3,4,4];
        console.log(Array.from(new Set(nums))); // [5, 1, 2, 3, 4] 注意必须要转为set类型,否则无效
        nums = [5,1,1,2,2,3,3,4,4];
        console.log( nums.filter((n, i) => {
        return nums.indexOf(n) === i // [5, 1, 2, 3, 4]
        }) );
    

六、正则表达式

  1. 做一个匹配首字符是非数字的,后面的字符是数字的正则表达式

    	const reg = /^d+[^d]+/
    	console.log(reg.test(1234)); // false
        console.log(reg.test('123a')); // false
        console.log(reg.test('d1231')); // true
        console.log(reg.test('a123bde')); // false
    
  2. 字符串的search和正则表达式的test的区别:search匹配到符合规格的数据,返回索引;test匹配到符合规格的数据,返回布尔值

    const reg = new RegExp(2);
    console.log(reg.test('123'));  // true
    console.log("123".search(reg)); // 1
    console.log("345".search(reg)); // -1
    
  3. 使用reg.exec()拿到匹配结果

    	const reg1 = /\d/
        console.log(reg1.exec("qwe1234rty")); // ['1', index: 3, input: 'qwe1234rty', groups: undefined]
        console.log(reg1.exec("qwerty")); // null
    
  4. str.match(reg)和reg.exec(str)都相同,只要匹配到符合规格的数据就返回数组,否则就返回null

        console.log("qwe1234rty".match(reg1)); // ['1', index: 3, input: 'qwe1234rty', groups: undefined]
        console.log("qwerty".match(reg1)); // null
    

七、其它

  1. 编写高性能javascript

    • 遵循严格模式:"use strict"
    • 将js脚本成组打包,减少请求,尽量减少使用闭包
    • 使用非堵塞方式下载js脚本,最小化重绘和回流
  2. 请写出格式化日期格式为2023-12-15的函数

    let datatime = new Date();
    let formData = function(datatime) {
        let year = datatime.getFullYear();
        let month = datatime.getMonth() + 1;
        let data = datatime.getData();
        month = month < 10 ? '0' + month : month;
        data = data < 10 ? '0' + data : data;
        return year + '-' + month + '-' + data;
    }
    
  3. 如何最小化重绘repaint和回流reflow

    • 对元素进行复杂操作时,先隐藏(display:none),操作完再显示
    • 需要创建多个DOM节点时,使用DocumentFragment创建完后一次性的加入document
    • 尽量避免用table布局(table元素一旦触发回流就会导致table里所有的其它元素回流)
  4. js原型:使用Object.create(null)没有原型

    	let emobi = Object.create(null)
        console.log(emobi); // 没有原型
        console.log([]); // 有原型
        console.log({}); // 有原型
    
  5. DOM

    • 不支持冒泡的鼠标事件:onmouseleave、onmouseenter

    • 阻止默认事件的默认操作的方法是:preventDefault

    • 事件传播的三个阶段:捕获 ——> 目标 ——> 冒泡

    • onblur:失去焦点 | onfocus 获得焦点

    • 添加子节点

      parentNode.appendChild(newNode)
      
    • 获取地理位置所在的经纬度

      console.log(Geolocation.getPosition());
      
  6. 箭头函数

    • 箭头函数没有原型属性
    • 箭头函数不绑定this,会捕获其所在的上下文的this值,作为自己的this值
    • 箭头函数不绑定arguments,取而代之用rest参数解决
    • 箭头函数不能用new创建一个箭头函数实例
  7. Promise

    • 在所有的 Promise 对象都成功解决后才会将一个成功的结果数组作为解决值返回,如果有一个 Promise 对象被拒绝了,则会立即将一个拒绝原因作为拒绝值返回。

      let p1 = Promise.resolve('aaa')
      let p2 = Promise.resolve('bbb')
      let p3 = Promise.reject('ccc')
      let p4 = Promise.resolve('ddd')
      Promise.all([p1, p2,p3, p4]).then(res => {
          console.log(res); //如果没有p3,即整个promise成功,那么返回数组
      }).catch(err => {
         console.log('error',err); // ccc 一旦返回数据失败,就停止并输出失败数据
      })
      
      // let p5 = Promise.reject('aaa')
      let p6 = Promise.resolve('bbb')
      // let p7 = Promise.reject('ccc')
      let p8 = Promise.resolve('ddd')
      Promise.race([ p6, p8]).then(res => {
          console.log('成功', res); //bbb 如果没有reject,返回第一个请求结果
      }).catch(err => {
           console.log('失败', err); // aaa 一旦返回数据失败,就停止并输出失败数据
      })
      
    • Promise.all() 适用于当我们需要等待多个异步操作全部完成,然后再进行下一步操作的场景。

      • 例如,我们需要从多个 API 请求中获取数据,然后将这些数据合并成一个结果
      • 在这种情况下,我们可以使用 Promise.all() 来等待所有的请求都完成,然后将结果合并起来。
    • Promise.race() 适用于当我们需要等待多个异步操作中的其中一个完成,然后再进行下一步操作的场景。

      • 例如,我们需要从多个 API 请求中获取数据,但只需要获取其中一个请求的结果即可。
      • 在这种情况下,我们可以使用 Promise.race() 来等待其中一个请求完成,然后处理其结果。
  8. 结构赋值

    	let [zz,xx,cc,vv,bb] = [1,2,3,4,5]; 
        console.log([zz,xx,cc,vv,bb] );
    

多选题

一、js基础

  1. ox十六进制;1e2 表示10的2次方乘以1

    	let aa = 0xa1; // 161 a1对应的二进制为10100001,十进制为161
        // aa = 076; 报错
        // aa = 0b21 报错
        aa = 7e2; // 700
        console.log(aa);   
    
    	console.log(typeof 17n); // bigint
    
  2. isNaN判断

    	console.log(NaN === 'number'); // false     
        console.log(isNaN('abc')); // true
        console.log(isNaN(NaN)); // true
    

二、ES6

  1. let和const都不存在变量提升,存在暂时性死区,只能在声明位置后面使用

    a = "a" // 报错
    let a 
    
  2. Promise.then的回调函数中,可以返回一个新的Promise

三、DOM

  1. 设置元素样式

    document.body.style.fontSize = '16px'
    document.body.style.setProperty('background-color','#fff')
    document.body.style = 'background-color: #fff'
    // document.body.style.['background-color'] = '#fff'
    
  2. 阻止冒泡事件的三种方法

    event.cancelable = true
    event.stopPropagation();
    return false;
    
    // 该方法只能阻止默认事件,比如点击连接跳转,但不能阻止冒泡事件发生
    event.preventDefault();
    

填空题

一、类型检测

  1. typeof检测的结果是string类型

    var x = typeof x
    var res = typeof typeof x
    console.log(x,res); // undefined,string
    console.log(typeof typeof 0); // string
    
  2. 使用Object.prototype.toString.call(type) ,返回一个数组,分别为type的类型和它的具体类型

    var ar1 = []
    console.log(typeof ar1,Object.prototype.toString.call(ar1)); // object [object Array]
    console.log(Object.prototype.toString.call(undefined)) // [object Undefined]
    

二、类型转换

  1. 字符串+数字 是拼接方式,无论先后位置

     console.log("2"+3+4) // 234
     console.log('5'+3,5+'3') // 53 , 53
    
  2. parseInt的参数为1个,有多个参数时返回NaN

    console.log(parseInt("111办公室")) // 111 
    console.log(parseInt('77',40)); // NaN
    
  3. 使用map返回数组

    console.log(["0x1","0x2","0x3"].map(parseInt)); // [1, NaN, 0]
    console.log([2,3,4].map((item)=>item > 2)); // [false,true,true]
    

三、逻辑判断

  1. 连续数值的大小比较,可以用||拆分开

    console.log([5<6<3,3<2<4]); // [true,true]
    console.log(5<6 || 6<3); // true
    console.log(3<2 || 2<4); // true
    
    // &&优先级高于||
    console.log(true||false&&false, true&&false||true) // true true
    
  2. 有多个 + - 符号连接的两个数,看作为正负号

    console.log(1 + - - - - + + - + - - 1 ); // 0 后面的1的前面的+ - 都看作是正负符号
    

程序题

一、js基础

  1. new String(c); 拿到的是一个对象

    console.log(new String('A')); // {'A'}
    console.log((new String('A')) instanceof Object);// true
    
  2. 修改字符串的长度不会修改掉字符串本身

    let strq = "我非常喜欢编程";
    strq.length = 3;
    console.log(strq); // 我非常喜欢编程
    
  3. 非严格模式下,函数的arguments数组里面的内容会随着函数内赋值而变化;而严格模式下,arguments不会随着函数内赋值而改变

    	function side(arr) {
              arr[0] = arr[2];
          }
          function func1(a, b, c = 3) {
              // 有参数的赋值,属于严格模式
              c = 10;
              side(arguments);
              console.log(arguments); // [1,1,1,...]
              console.log(a + b + c);
          }
          function func2(a, b, c) {
              c = 10;
              side(arguments);
              console.log(arguments); // [10,1,10,...]
              console.log(a + b + c);
          }
          func1(1, 1, 1); // 12
          func2(1, 1, 1); // 21
    
  4. 使用new Number(x) 同样拿到的是一个对象Object

    	var g = 3;
        var h = new Number(3);
        var j = 3;
        console.log(h instanceof Object); // true
        console.log(g == h); // true
        console.log(g === h); // false
        console.log(h === j); // false
    
  5. arr.shift()去掉首项元素 ; arr.splice(0,1,3) 从索引0开始删除一个元素并在末尾加上一个元素3

  6. 小数之和会有误差

    console.log(0.1 + 0.2);  // 0.30000000000000004
    
  7. 其它

    console.log(typeof Function); // function
    console.log(typeof Object); // function
    console.log(typeof {}); // objectqita
    console.log('lk' + 1); // lk1
    console.log('lk' - 1); // NaN
    console.log(Function instanceof Object); // true
    
  8. class属于function类型

    console.log(typeof function () {} ); // function 
    console.log(typeof class {}); //  function    
    console.log(Object.prototype.toString.call(class{})); // [object Function]
    
  9. 剩余参数语法:... 将所有参数都收集到一个数组中

    function getAge(...args) { 
        console.log(typeof args);
        // ...args剩余参数语法,将所有参数都收集到一个数组中
        // console.log(args instanceof Array); // true
    }
    getAge(21); // object 
    

二、js深入

  1. 对this的判断

    var xxx = 1;   
    var OBJ = {
        xxx: 3,
        fun:function () {
            var xxx = 5;
            return this.xxx;
        }
    };
    var fun = OBJ.fun;
    console.log( OBJ.fun(), fun() ); // 3 1
    // 第一次this指向OBJ这个对象 第二次this指向全局window
    
  2. 对箭头函数中this的理解

    function NEWfun () {
        return () => {
            return () => {
                return () => {
                    console.log(this.name)
                }
            }
        }
    }
    var foo = NEWfun.call({name: 'foo'})
    var t1 = foo.call({name: 'bar'})()() // foo 
    var t2 = foo().call({name: 'baz'})() // foo 
    var t3 = foo()().call({name: 'qux'}) // foo 
    // 箭头函数的this是继承父级作用域的this,不是指向调用者
    // 第一句:把this指向{name:'foo'}
    // 之后的句子返回的都是箭头函数,使用call()不能改变this指向,作用域链上的this还是指向{name:'foo'}
    
    var jb = 2;
    let jbo = {
        jb: 1,
        foo: () => {
            console.log(this.jb) 
        }
    }
    const jbo1 = jbo.foo
    // log1
    jbo.foo() // 2
    // log2
    jbo1()  // 2
    // 箭头函数始终指向外面
    
  3. 构造函数的默认参数

    const Persont = function(name="wang",age=10) {
    	this.name = name;
    	this.age = age;
    	return this.name +' is '+ this.age + 'years old'
    }
    let pers = new Persont('zhang',11)
    console.log(pers) // Persont {name: 'zhang', age: 11}
    
  4. new绑定优先级高于bind绑定

    var global = 'global';
    var obj5 = {
       global: 'local',
       foo1: function(){
           this.global = 'foo';
       }.bind(window) // 由于new绑定优先级大于bind绑定,所以此时函数内部的this还是obj5
    }
    
    var bar = new obj5.foo1();
    setTimeout(function() {
        console.log(window.global); // global
    }, 0);
    console.log(bar.global); // foo
    
    var bar3 = bar2 = bar;
    bar2.global = 'foo2';
    console.log(bar3.global); // foo2
    

三、js事件循环

  1. promise.then()方法,只有在resolve返回结果之后才会参数,如果.then().then()中第一个then没有返回结果,那么第二个then拿到的参数为undefined

    	const promiseA = Promise.resolve('a')
        promiseA.then((res) => {
            console.log(res) // a
        }).then((res) => {
            console.log(res) // undefined
        })
        // 第二个then中的回调函数会接收前一个'then'返回的结果
    
        const promiseB = Promise.resolve('b')
        promiseB.then((res) => {
            console.log(res) // b
        })
        promiseB.then((res) => {
            console.log(res) // b
        })
    
  2. 事件循环:先处理执行栈中的事件,再处理任务队列中的事件:这期间微任务要比宏任务先执行

    微任务:

    • promise.then()
    • proness.nextTick(Node环境)
    • MutationObserver(监听DOM树变化)

    宏任务:

    • script整体代码;
    • setTimeout,setInterval;
    • UI渲染;
    • I/O,setImmediate(Node环境)
    	setTimeout(() => {
            console.log(1)
        }, 0)
        console.log(-1)
        const P = new Promise((resolve, reject) => {
            console.log(2) // 这里等同于log-1
            setTimeout(() => {
                resolve()
                console.log(3)
            }, 500)
        })
        
        P.then(() => {
            console.log(4)
            // 等P执行完毕后才会执行
        })
        console.log(5)
    
        // -1 2 5 1 3 4
    
  3. 无论宏任务在哪里,都是在微任务之后执行

    (async () => {
        console.log(1);
        setTimeout(() => {
            console.log(2);
        }, 0);
        await new Promise((resolve, reject) => {
        console.log(3);
        // resolve(); 只有在resolve执行完毕,才会输出4 5
        }).then(() => {
            console.log(4);
        });
        console.log(5);
    })();
    
  4. promise还没有执行回调then() 函数的时候,会和执行栈中的事件同步操作

        new Promise((resolve) => { // new Promise执行是同步的
            console.log('1')
            resolve()
            console.log('2')
        }).then(() => {
            console.log('3')
        })
        setTimeout(() => {
            console.log('4')
        })
        console.log('5')
        // 1 2 5 3 4
    
  5. 冒泡事件:会先执行微任务(包括父元素事件)完毕后,再执行宏任务

    <body>
        <div class="outer">
            <div class="inner">hahah</div>
        </div>
    <script>
        var outer = document.querySelector('.outer');
        var inner = document.querySelector('.inner');
    
        function onClick() {
            console.log('click');
    
            setTimeout(function() {
                console.log('timeout');
            }, 0);
    
            Promise.resolve().then(function() {
                console.log('promise');
            });
        }
        // click promise click promise timeout timeout
        // 冒泡事件
    
        inner.addEventListener('click', onClick);
        outer.addEventListener('click', onClick);
    
    </script>
    

四、ES6

  1. 给对象增加属性的函数:Object.defineProperty()

  2. 拿到对象的key值:Object.key(obj)

    const student = {name: 'ZhangSan'}
    Object.defineProperty(student, 'age', {value: 22})
    console.log(student) // {name: "ZhangSan";age: 22}
    console.log(Object.keys(student)) // ["name"]
    
  3. generator

    function * cb(x, y) {
        for(let i = Math.ceil(x); i <= y; i++) {
            yield i;
        }
    }
    var a = cb(6, 9);
    console.log(a.next()); // {value: 6, done: false}
    console.log(a.next()); // {value: 7, done: false}
    // 当a到9以后,done=true
    // 1.函数生成器特点:function * fun(){}
    // 2.调用函数生成控制器
    // 3.通过next()方法执行函数
    // 4.遇到yield函数暂停
    // 5.next()继续执行
    // 6.使用let每个i都生成一个作用域,互补干扰,保留当时的赋值
    
  4. 扩展运算符 ...args

        function fn(...args) {
          console.log(typeof args);
        }
        fn(21);
        // Object
    
  5. class使用ES6代码编译

    // class
        class Person {
            constructor (name) {
                this.name = name;
            }
            greet () {
                console.log(`Hi, my name is ${this.name}`);
            }
            greetDelay (time) {
                setTimeout(() => {
                    console.log(`Hi, my name is ${this.name}`);
                }, time);
            }
        }
        // ES6代码编译后所生成的ES5代码
        function Person1(name) {
            this.name = name
        }
        Person1.prototype = {
            greet: function() {
                console.log('Hi, my name is '+this.name);
            },
            greetDelay: function(time) {
                var that = this
                setTimeout(function() {
                    console.log("Hi, my name is "+that.name);
                },time)
            }
        }
    
  6. 使用标签模板

        function getPersonInfo(one, two, three) {
            console.log(one)
            console.log(two)
            console.log(three)
        }
        const person = 'Lydia'
        const age = 21
        getPersonInfo
        `${person} is ${age} years old` // Lydia 21
    

五、js作用域

  1. let、const块级作用域

    • 不存在变量提升
    • 存在暂时性死区问题
    function sayHello() {
            console.log(name);
            console.log(age);
            var name = "Tom";
            let age = 18;
        } 
        sayHello();
        // undefined 报错
    
  2. 用var声明的变量,在全局范围内有效; 用let声明的变量,每次都会新声明一个作用域

    for (var i = 0; i < 3; i++) {
         setTimeout(_ => {
             console.log(i)
         })
     }
     // 3 3 3
     // 全局中只有一个变量i,每次循环时,setTimeOut定时器里指的是全局变量i,而循环里的setTimeOut是在循环结束后才执行,所以输出33
     for (let i = 0; i < 3; i++) {
         setTimeout(_ => {
             console.log(i)
         })
     }
     // 0 1 2 
    
  3. 对象1中的对象2中的this指向对象2

     "use strict"
        var name = 'Jay'
        var person = {
            name: 'Wang',
            pro: {
                name: 'Michael',
                getName: function () {
                    console.log(this.name);
                    return this.name
                }
            }
        }
        person.pro.getName() // Michael this指向了{name: 'Michael', getName: ƒ}
        var people = person.pro.getName 
        people() // Jay
    
  4. 变量声明都会被提到作用域顶部,但是赋值仍然在其原来的位置

    compute(10,100); // 220
        var compute = function(A,B) {
            console.info(A * B) ;
        };
        function compute(A,B){
            console.info(A + B);
        }
        function compute(A,B){
            console.info((A + B)*2);
        }
        compute(2,10); // 20
    
        meili()  // meili
        function meili() {
            console.log("meili") 
        }
        mogu() //  mogu is not a function
        var mogu = function() {
            console.log("mogu")
        }
    

六、js原型链

  1. 在实例中没有找到属性,就去原型函数上找

    function Fn1(name) {
        if(name) {
            this.name = name;
        }
    }
    Fn1.prototype.name="jack"
    let a = new Fn1();
    console.log('a:', a.name); // a:jack
    
    function Fn2(name) {
        this.name = name;
    }
    Fn2.prototype.name="jack"
    let b = new Fn2(); 
    console.log('b:', b.name); // b:undefined
    
        // 定义实例a,没有传入参数,判断name为false,所以无法添加name属性,所以会沿着原型链去找,于是打印了Fn1.prototype.name
        // 定义实例b,没有传入参数,会给this.a赋值为undefiend,b上面就有了name属性,打印b.name
    
  2. 原型中的变量是全局变量

    var Foo = (function() {
            var x = 0;
            function Foo() {}
            Foo.prototype.increment = function() {
                ++x;
                console.log(x);
            };
            return Foo;
        })();
        
        var a = new Foo();
        a.increment(); // 1
        a.increment(); // 2
        var b = new Foo();
        b.increment(); // 3
    
        // 注意到Foo()本身已经立即执行了,所以x变量是会跟着改变的
        // increment 函数可以访问到 x 变量
    
  3. 非严格模式下,函数的this指向window

        var name = 'Jay'
        function Person(name){
            this.name = name;
            console.log(this.name)
        }
        var a = Person('Tom') // Tom
        console.log(name) // Tom
        console.log(a) // undefined
        var b = new Person('Michael') // Michael
        console.log(b) // Person {name:'Michael'}
        // 因为Person('Tom')默认绑定,非严格模式下this指向window,修改全局name='Tom'
        // Person('Tom')是没有返回值的,所以为undefined
        // 此时this指向b,参数为Michael
    
  4. 实例._ proto_ = 构造函数.prototype

    var tmp = {};
        var A = function() {};
        A.prototype = tmp;
        var a = new A();
        A.prototype = {};
        
        var b = Object.create(tmp);
        b.constructor = A.constructor;
        console.log(a instanceof A);
        console.log(b instanceof A);
        console.log(b);
        // false
        // false
    
        // var a = new A()时,a.__proto__指向A.prototype,即tmp
        // A.prototype = {},此时A指向了新的 空对象 {}
    
        // b.prototype 指向tmp
        // Ab的构造函数相同,但是原型不同
    
  5. 继承关系

        class A{}
        class B extends A{}
        const a = new A()
        const b = new B()
        console.log(a.__proto__);  // constructor: class A
        console.log(b.__proto__);  // constructor: class B
        console.log(B.__proto__); // class A{}
        console.log(B.prototype.__proto__); // constructor: class A
        console.log(b.prototype.__proto__); // 报错
    

七、其它

  1. computed有缓存功能,当其数据不变时,就不会进行操作

    var vm = new Vue({  
        el: '#example',  
        data: {    
            message: 'Hello'  
        },  
        computed: {    // 有缓存功能,message只更新一次,所以1只输出一次
            test: function () {      
                console.log(1)      
                return this.message    
            }  
        },  
        created: function (){    
            this.message = 'World'    
            for (var i = 0; i < 5; i++) {        
                console.log(this.test)    
            }  
        }
    })