面试题总结liebao

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

  • define一个模块
// a.js
define(function(){
  return {
    name: 'a'
  }
})
// b.js
define(function(){
  return {
    name: 'b'
  }
})
  • 引入模块
// index.html

  • 目录结构


    面试题总结liebao_第1张图片
    屏幕快照 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循环和闭包, 创建命名空间防止变量污染
  • 描述创建对象的不同方式, 和各自影响, 提供示例


    面试题总结liebao_第2张图片
    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)