美文网首页我爱编程
面试题总结liebao

面试题总结liebao

作者: 胖太_91bf | 来源:发表于2018-05-28 08:21 被阅读0次

    1. 跨域问题

    • jsonp: 利用script标签不受同源策略限制的特点, 协商好一个callback参数, 当然这个参数是由请求方定的
    • h5新方法: window.postMessage, 首先要有一个src是另一个域页面的ifrmae标签, 并且监听一个onload事件, 然后在onload函数里获取到这个ifrmae标签, 并通过.contentWindow获取到另一个域的页面的window对象, 使用这个window对象调用postMessage('XXX')方法, 传递数据, 最后在另一个域的页面里, 调用window.onmessage事件, 通过 事件对象e.data获取传递的内容
    • 还可以用服务器代理的方式, 我用过node和axios实现过
    • window.name: 它有一个特性就是, 在窗口生命周期没有结束之前(未关闭之前)都只会共享一个window.name, 这样, 当我在a.html里设置了一个window.name, 页面跳转b.html后, 这个window.name不会改变, 还是a.html里设置的, 就相当于把a.html里的数据传到了b.html中. 具体实现:在data.html页面设置好window.name利用ifrmae标签载入data.html页面, 当ifrmae载入date.html时候, 用一个函数用来获取data.html的window.name,

    2. 闭包

    • js分全局作用域和函数作用域, 通过作用域链, 函数作用域可以访问到包含环境和全局作用域, 全局作用域无法直接访问到函数作用域, 只有通过闭包(函数中的函数), 将父级里的数据return出去, 然后在把闭包return出去, 闭包能一直访问到父级的数据, 说明父级里的数据一直存在在内存中, 所以闭包可能会导致内存泄露
    • document.domain: 将两个页面的document.domain设置成一样的, 就可以通过ifrmae.contentWindow方法, 访问到通过ifrmae载入的页面的window对象了,也能传值了

    3. css3动画

    4. 页面性能问题解决和分析

    • 浏览器渲染的过程
      -- html转化为dom
      -- css转化成cssom
      -- 结合dom和cssom生成渲染树
      -- 生成布局 flow
      -- 将布局绘制到页面上 paint
      *注意: 用js获取offsetXXX, scrollTop等还有getComputedStyle()的时候会立即出发回流(reflow), 所以不要吧这些读操作和写操作放到一个语句里
      *原则: 样式表越简单, 回流和重绘越快. dom层级越高, 成本就越高. table成本高与div
    • 提高性能技巧:
      -- 读操作写在一起, 写操作也在一起, 两种操作不要混在一起
      -- 样式是通过回流(重排)得到的, 最好缓存下
      -- 不要一条条改变样式, 尽量操作class
      -- 如果需要对摸个元素进行多次操作, 可以先将其设置成display:none, 在随便操作n次, 完事后再display: block
      -- 使用虚拟dom脚本, 例如react或vue
      -- window.requestAnimationFrame()调节重绘
    • js优化:
      -- 减少作用域链查找, 在当前执行环境缓存一下包含环境或全局执行环境的数据;
      -- 字符串拼接尽量避免使用 + , 使用数组的join();
      -- 优化循环, 简化循环体, 简化终止条件, 比如 for(var i = 0,len= arr.length; i < len; i ++);
      -- switch语句更快;
      -- 变量声明提倡 一个var
      -- 使用事件委托js用addlistenerEvent, jq用 on
    • 减少http请求, 合并css,js, 使用雪碧图,
    • 图片懒加载
    • 静态资源用cdn
    • 按需加载资源requirejs

    5. 用户交互设计理论

    6. js事件绑定

    7. 遇到比较难的问题, 怎么解决的

    8. 移动端开发经验

    9. canvas

    10. js作用域 - 函数作用域链

    11.原型继承:

    // Student的构造函数
    function Student(props) {
      this.name = props.name || 'nonamed'
    }
    Student.prototype.sayHello = function() {
      alert('Hello ' + this.name)
    }
    // 通过Student扩展出PrimaryStudent
    function PrimaryStudent() {
      Student.call(this, props)
      this.grade = props.grade || 1
    }
    // 声明空对象
    function F() {}
    // 把空对象原型指向Student
    F.prototype = Student.prototype
    // 把PrimaryStudent的prototype指向F的实例化对象
    PrimaryStudent.prototype = new F()
    // 修复PrimaryStudent上的构造函数(constructor)
    PrimaryStudent.prototype.constructor = PrimaryStudent
    // 验证
    var xiaoming = new PrimaryStudent({name: 'zxk', grade: 10})
    xiaoming.name // 'zxk'
    xiaoming.grade // 10
    // 验证原型:
    xiaoming.__proto__=== PrimaryStudent.prototype // true
    xiaoming.__proto__ .__proto__=== Student.prototype // true
    // 验证继承:
    xiaoming instanceof PrimaryStudent // true
    xiaoming instanceof Student // true
    

    12 排序算法

    // 冒泡排序
    function sort(arr) {
            for(var i = 0; i < arr.length; i ++) {
                for(var j = 0; j < arr.length; j ++) {
                    if (arr[j] > arr[j + 1]) {
                        var oldJ = arr[j]
                        arr[j] = arr[j + 1]
                        arr[j + 1] = oldJ
                    }
                }
            }
        }
    // 
    

    13 将[1, 2, [3, [4, 5, [6]]]] => [1, 2, 3, 4, 5, 6]

    function transArr(arr, res) {
      var res = res || []
      for(var i = 0; i < arr.length; i ++) {
        if(arr[i] instanceof Array) {
          transArr(arr[i], res)
        } else {
          res.push(arr[i])
        }
      }
      return res
    }
    

    14 事件委托

    • 好处, 减少dom操作, 提高性能, 为动态插入的元素绑定事件
    • 原理事件冒泡, js -> addlistenerEvent, jQ -> on
    • 使用on去绑定事件, 通过e.target获取实际点击的元素进行相应的操作
    • jQ使用on去绑定事件, 第二个参数填选择器, 会将父级事件代理到这个选择器上(使用与动态插入的dom)

    15 flex的兼容

    • 旧版: display: box; 过渡: display: flex box; 新版: display: flex
    • 安卓: 2.3+ display: -webkit-box; 4.4+ display: flex
    • ios: 6.1+ display: -webkit-box; 7.1+ display: flex
    • pc: ie10支持 flex, -ms形式
    • 兼容写法, 都是向下兼容的, so旧版写法要放到下边, 否则就会不起作用
    .box{
    
        display: -webkit-flex;  /* 新版本语法: Chrome 21+ */
        display: flex;          /* 新版本语法: Opera 12.1, Firefox 22+ */
        display: -webkit-box;   /* 老版本语法: Safari, iOS, Android browser, older WebKit browsers. */
        display: -moz-box;      /* 老版本语法: Firefox (buggy) */
        display: -ms-flexbox;   /* 混合版本语法: IE 10 */   
     }
    .flex1 {            
        -webkit-flex: 1;        /* Chrome */  
        -ms-flex: 1             /* IE 10 */  
        flex: 1;                /* NEW, Spec - Opera 12.1, Firefox 20+ */
        -webkit-box-flex: 1     /* OLD - iOS 6-, Safari 3.1-6 */  
        -moz-box-flex: 1;       /* OLD - Firefox 19- */       
    }
    

    16 模块化requirejs

    • 引入require.js
    • 配置main.js
    require.config({
      baseUrl: 'js',
      paths: {
        app: 'app' // 如果是目录的话, 这里的名字要和目录一模一样, 如果是文件可以不同
      }
    })
    
    • 在html里引入requirejs和data-main入口
    //index.html
    <script src="js/requirejs" data-main="main"></script>
    
    • define一个模块
    // a.js
    define(function(){
      return {
        name: 'a'
      }
    })
    // b.js
    define(function(){
      return {
        name: 'b'
      }
    })
    
    • 引入模块
    // index.html
    <script>
        require(['app/a'], function(a) {
            console.log(a)
        })
        setTimeout(function() {
            require(['app/b'], function(b) {
                console.log(b)
            })
        }, 3000)
    </script>
    
    • 目录结构


      屏幕快照 2018-05-26 下午12.57.10.png

    17 css布局

    • 左边固定宽度, 右边自适应
      • 左边: width: 100px; float: left;右边: width: auto;margin-left: 100px;
      • flex: 左边固定宽度, 右边: flex: 1,
    • 三栏布局左右固定宽度, 中间自适应
      • 结构是: 左 - 右 - 中, 左: 固定宽度, float: left; 右: 固定宽度float: right; 中: width: auto; margin-left: 左宽, margin-right: 右宽
    • css选择器:
      • 类选择器.class
      • id选择器: #id
      • 标签选择器: p
      • 后代选择器: div p
      • 子代选择器: div>p
      • 之后选择器: div+p
      • 属性选择器:
    • 伪类选择器:
    • nth-child()
    • first-child
    • :before - after

    18 for循环和定时器 与 闭包

    //瞬间打出0 - 4
        for(var i = 0; i < 5; i++) {
            console.log(i)
        }
        for(var i = 0; i < 5; i++) {
            setTimeout((function(){ // 自调用函数, 立即执行
                console.log(i)
            })(), i * 1000)
        }
        // 顺间打出0, 然后每隔一秒打出1-4
        for(var i = 0; i < 5; i++) {
            (function(i){
                setTimeout(function() {
                    console.log(i)
                }, i*1000)
            })(i)
        }
        // 顺间打出5, 每隔1秒打出一个5, 共4个
        for(var i = 0; i < 5; i++) {
            setTimeout(function() {
                console.log(i)
            }, i*1000)
        }
        for(var i = 0; i < 5; i++) {
            (function(){
                setTimeout(function() {
                    console.log(i)
                }, i * 1000)
            })(i)
        }
    // promise相关
        setTimeout(function() {
            console.log(1)
        }, 0)
        new Promise(function executor(resolve){
            console.log(2)
            for(var i = 0; i < 1000; i++) {
                i == 999 && resolve(); 
            }
            console.log(3)
        }).then(function() {
            console.log(4)
        })
        console.log(5)
        // logs 2 > 3 > 5 > 4 > 1, promise的then()会放在当前tick的最末尾, 但是setTimeout会把函数踢到下一轮tick中
    

    18 js高阶面试

    • 描述js中的继承和原型链, 并举例
      1. js并没有类的概念, 而是通过原型链实现的继承, 每个对象都会在内部引用一个prototype的对象, prototype也会引用自己的原型对象, 以此类推, 链条的末尾是null为原型的对象, 当一个对象引用了不属于自己的属性时,将会遍历原型链, 直到找到属性,或者找到链尾 null
    • js中的对象和哈希表
      1. 对象本质就是一个hash表, 即键值对的集合, 键总是字符串, 由于js是弱类型语言, 当你传入的键并非字符串时候, 也不会报错, 而是隐式的使用toString()方法,
      2. 题目:
      var foo = new Object()
      var bar = new Object()
      var map = new Object()
      map[foo] = 'foo'
      map[bar] = 'bar'
      console.log(map[foo]) // logs: 'bar'
      解释: 由于foo和bar都不是,字符串, js会隐式的将foo和bar使用toString()方法,由于它们都是空对象, 所以它们转化的结果一样: [Object Object], 也就是说map[bar]覆盖了map[foo], map[foo]的结果也是覆盖后的结果
      
    • 解释js中的闭包, 什么是闭包, 它们有什么特性, 如何使用, 举个例子
      1. 闭包是一个函数, 包含在创建闭包时处于作用域内的所有变量或其他函数. 闭包通过'内部函数'的形式实现的, 也就是另一个函数的主体内定义函数
      2. 闭包的一个特性: 闭包可以一直访问其包含函数的变量, 也就是说他们会一直存在在内存中不被释放
      3. 闭包可以防止变量被内存释放, for循环和闭包, 创建命名空间防止变量污染
    • 描述创建对象的不同方式, 和各自影响, 提供示例


      91527395499_.pic.jpg
    • 函数表达式(var foo = function (){})和函数语句(function foo(){})定义函数的区别?
      1. 函数语句可以在定义之前被调用(通过hoisting技术, 总是使用函数的最后一个定义),
      function foo(){ return 1}
      console.log(foo()) // 结果为: 2
      function foo() {return 2}
      
      1. 函数表达式, 则不能在函数定义之前被调用
    • 将 js源文件里的内容封装到一个函数里, 这样做的重要性和原因?
      1. 相当于为它创建了一个闭包, 创建一个私有的命名空间,避免命名冲突
      2. 为全局变量提供一个容易引用的别名
      (function($){
         ...
      })(jQuery)
      
    • =====有什么区别, 举个例子
      1. == 比较前会有隐式转换, 而 ===是严格的类型比较
      2. 123 == '123'是成立的结果为true, 123 === '123'是不成立的, 结果为false
    • js文件开头的use strict是什意思?
      1. js的严格模式, 对js代码执行严格的解析和错误处理, 没有直接调用者的函数, this指向'undefined'

    19 js中的this指向(非严格模式下)

    函数中的this在函数被定义的时候并没有确定下来, 只有函数被调用的时候,才知道它到底指向谁

    • 原则1: 一个函数没有被上级调用, 那么this指向window
      function foo() {
        console.log(this) // window
      }
      foo()
      
    • 原则2: 一个函数被上级调用, 那么this指向上级函数
      var o = {
            a: 10,
            c: function() {
                console.log(this.a) // 10
                console.log(this) // o
            }
        }
      o.c()
      
    • 原则3: 一个函数中包含多个对象, 尽管这个函数是被最外层的对象调用, 但是this也只会指向它的上级
      var o = {
            a: 10,
            b: {
                a: 12,
                fn: function() {
                    console.log(this.a) // 12
                    console.log(this) // b
                }
            }
        }
      o.b.fn()
      
    • 原则4:
      var o = {
        a: 10,
        b: {
          a: 12,
          fn: function(){
            console.log(this.a) // undefined
            console.log(this) // window
          }
        }
      }
      var j = o.b.fn
      j() 
      
      • 原则5: 构造函数中的this: new 可以改变对象的this指向 -> new后边的函数
      function Fn() {
        this.user = 'zxk'
      }
      var a = new Fn()
      console.log(a.user) // 'zxk'
      
    • 原则6: this和return
      1. 构造函数里return {}, this指向, 这个对象
      function Fn() {
            this.name = 'zxk'
            return {
                name: 'ret - zxk'
            }
        }
        var xx = new Fn()
        console.log(xx.name) // 'ret - zxk'
      
      1. 构造函数里return function(){}, this指向, 这个函数,但是没有继承这个函数的prototype, 打印出来的是空
       function Fn() {
            this.name = 'zxk'
            return function() {}
        }
        var xx = new Fn
        console.log(xx.name) // 啥也没有
      
      1. 构造函数里return 非对象, this指向原来的这个构造函数
      function Fn() {
            this.name = 'zxk'
            return 1
        }
        var xx = new Fn()
        console.log(xx.name) // 'zxk'
      
    • call, apply和bind
      • 改变函数的this执行, 并没有继承原型链
      • call, apply会立即执行, bind需要()调用
    • 常用的字符串和数组方法:
      • slice(start, end): string直接返回重start开始到end(不包括start)的部分,可以为负
      • splice(index, howmany, item1, item2....,itemn): 从数组的index开始, 删howmany个元素, 替换成item1 - itemn
      • concat() 合并两个数组
      • substring(start, stop): 返回字符串start到stop(不包含)之间的字符, 不接受负值, 如果stop为负值, 那么就取start之前的值
      • substr(start, [length]): 返回字符串从start开始的, 共计length个, 可以接受负值

    20 js经典面试题

    // code 1
    var length = 10;
    function fn() {
      alert(this.length);
    }
    var obj = {
      length: 5,
      method: function() {
        fn();
      }
    };
    obj.method(); // 最后相当于fn(), 函数没有被任何上级调用, this指向window
    // code 2
    var num = 100;
    var obj = {
      num: 200,
      inner: {
        num: 300,
        print: function() {
          console.log(this.num);
        }
      }
    };
    
    obj.inner.print(); //300, this指向inner
    
    var func = obj.inner.print;
    func(); //100 默认绑定,this指向window
    
    obj.inner.print(); //300 thi指向inner
    
    (obj.inner.print = obj.inner.print)(); //100 this指向window
    // code 3
    function foo() {
      console.log(this.a);
    }
    var obj2 = { a: 42, foo: foo };
    var obj1 = { a: 2, obj2: obj2 };
    obj1.obj2.foo(); // 42 this指向他的直接上级obj2
    
    var obj3 = { a: 2 };
    foo.call(obj3); // 2, this指向call的参数obj3
    
    var bar = function() {
      foo.call(obj3);
    };
    bar(); // 2, 就相当于foo.call(obj3)
    setTimeout(bar, 100); // 2, bar的this指向window, 但是执行bar的时候, 其实执行的是foo.cal(obj3), 
    bar.call(window); // 2, 同上
    
    var obj4 = { a: 3, foo: foo };
    obj2.foo(); // 42, this指向obj2
    obj4.foo(); // 3, this指向obj4
    obj2.foo.call(obj4); // 3 this指向call的参数obj4
    obj4.foo.call(obj2); // 42 this指向call参数obj2
    // code 4
    function foo() {
      console.log(this.a);
    }
    var obj = {
      a: 2,
      foo: foo
    };
    var a = "oops, global"; // a是全局对象的属性
    setTimeout(obj.foo, 100); // "oops, global", setTimeout里的this都会指向window, 相当于var fun = obj.foo, fun()
    obj.foo(); // 2, this指向obj
    
    // code 5 (new绑定)
    function foo(a) {
      this.a = a;
    }
    var bar = new foo(2);
    console.log(bar.a); // 2 new可以改变this, 让bar的this指向foo
    
    var obj1 = { foo: foo };
    var obj2 = {};
    
    obj1.foo(2); 
    console.log(obj1.a); // 2, foo(2)的this指向obj1, this.a相当于给obj1添加a属性
    
    obj1.foo.call(obj2, 3);
    console.log(obj2.a); // 3,  obj1的this指向obj2并传参数, 相当于给obj2, 添加a属性
    
    var bar = new obj1.foo(4);
    console.log(obj1.a); // 2
    console.log(bar.a); // 4 
    
    // code 6
    
    function foo() {
      console.log(this.a);
    }
    
    var a = 2;
    
    // 如果你把null或者undefined作为this的绑定对象传入call\apply\bind,这些值在调用的时候会被忽略,实际应用的是默认绑定规则。
    foo.call(null); // 2
    var bar = foo.bind(null);
    bar(); // 2
    foo.apply(undefined); // 2
    
    // code 7 箭头函数
    
    function foo() {
      return a => console.log(this.a);
    }
    
    var obj1 = { a: 2 };
    var obj2 = { a: 3 };
    var bar = foo.call(obj1);
    bar.call(obj2); // 2 箭头函数是根据外层(函数或者全局)作用域来决定this。并且绑定后无法修改。
    
      1. 字符串倒序
      function reverseString(str) {
        var strArray = str.split(""); // 使用空字符串来分割成字符数组
        return strArray.reverse().join(""); // 反转并连接
      }
      reverseString("hello");
      
      1. 下面代码执行结果是啥, 如何实现每隔1秒输出1 - 5
      for (var i = 1; i <= 5; i++) {
        setTimeout(function() {
          console.log(i);
        }, i * 1000);
      }
      

      每个1秒秒输出6共输出5个, 循环终止条件是i <= 5, i最终值是6的时候, 循环才结束, setTimeout会把函数踢出本次任务队列, 等循环结束后, 才打印console.log(i), i那个时候已经是6了,

      • 套个闭包, 并把每次循环的i缓存起来,并传进闭包就ok
      • 用let声明 变量: 每次循环都会重新声明一个i
      for(let i = 0; i < 5; i++) {
            setTimeout(function() {
                console.log(i)
            }, i*1000)
        }
      
      1. 代码执行结果
      3 + "3" // '33'
      "23" > "3" // false
      var b = true && 2; // undefined
      "abc123".slice(2, -1) // 'c12'
      "abc123".substring(2, -1) // 'ab' //如果 start or stop 是负数或 NaN,会把它当成 0 对待;如果 start > stop,则会交换这两个参数
      
     var foo = 1,
     bar = 2,
     j,
     test;
    test = function(j) {
     j = 5;
     var bar = 5;
     console.log(bar); // 5
     foo = 5;
    };
    test(10);
    console.log(foo); // 5 改变的全局变量
    console.log(bar); // 2 由于函数作用域对全局作用域的隐藏,所以只有在test函数内部,bar=5,并不能影响到全局中的bar
    console.log(j); // undefined  test(10)函数调用的时候,是函数内部的参数j接收到了10,但是它也是函数作用域内的变量,并不会改变全局作用域中的j。
    
    if (!("sina" in window)) {
      var sina = 1;
    }
    console.log("sina:", sina); // undefined
    // 变量提升: js会把所有的通过var声明的全局变量, 提升到最顶层,  所有sina液在if之前就存在了, 也不会走if()分支
    // 相当于
    var sina
    f (!("sina" in window)) {
       sina = 1;
    }
    console.log("sina:", sina); // undefined
    
    function SINA() {
      return 1;
    }
    var SINA;
    console.log(typeof SINA); // function
    // 重复声明被忽略了, 所以var没有生效
    
    • 数组去重, 带obj的元素
    var arr = [2, [1,2], 3, "2", "a", "b", "a", [1, 2]]
      function quchong(arr) {
          var map = {}
          var res = []
          arr.forEach(item => {
              if(!map[JSON.stringify(item)]) {
                  res.push(item)
                  map[JSON.stringify(item)] = 1
              }
          })
          return res
      }
      console.log(quchong(arr))
    
    function foo() {
      "use strict";
      console.log(this.a);
    }
    
    function bar() {
      console.log(this.a);
    }
    
    var a = "this is a 'a'";
    
    bar(); // "this is a 'a'"
    foo(); // "TypeError: Cannot read property 'a' of undefined
    
    alert(a); // 输出函数体
    a(); // 10
    var a = 3;
    function a() {
      alert(10);
    }
    alert(a); // 3
    a = 6;
    a(); // 报错a不是一个function
    
    alert(a); // undefined
    a(); // a is not a function
    var a = 3;
    var a = function() {
      // 函数表达式
      alert(10);
    };
    alert(a); // 输出函数体
    a = 6;
    a(); // a is not a function 因为a已经赋值成6了
    
    • 查看字符串中最多的元素
    function findMax(str) {
          var map = {} // 存储str每个元素和对应数量的对象
          var max = {num: 0} // 存储最大值的对象
          for(var i in str) {
              if(map[str[i]]) {
                  map[str[i]] ++
              } else {
                  map[str[i]] = 1
              }
              if(map[str[i]] > max.num) {
                  max.num = map[str[i]]
                  max.key = str[i]
              }
          }
          return max
      }
      var max = findMax(_str)
      console.log(max)
    

    21 数组方法和字符串方法

    • 数组方法:
      • pop(): 删除数组最后删一个元素, 并返回这个元素
      • push(): 在数组末尾添加一个元素, 并返回数组的length

    相关文章

      网友评论

        本文标题:面试题总结liebao

        本文链接:https://www.haomeiwen.com/subject/bjqejftx.html