刷前端面经笔记(八)

1.apply,call,bind有什么区别?

三者都可以把一个函数应用到其他对象上, apply , call 是直接执行函数调用, bind 是绑定,执行需要再次调用。

applycall 的区别是 apply 接受数组作为参数,而 call 是接受逗号分隔的无限多个参数列表。

代码如下:

function Person() {
    }
    Person.prototype.sayName() { alert(this.name); }

    var obj = {name: 'michaelqin'}; // 注意这是一个普通对象,它不是Person的实例
    // 1) apply
    Person.prototype.sayName.apply(obj, [param1, param2, param3]);

    // 2) call
    Person.prototype.sayName.call(obj, param1, param2, param3);

    // 3) bind
    var liaoke = Person.prototype.sayName.bind(obj);    
    liaoke ([param1, param2, param3]); // bind需要先绑定,再执行 
    liaoke (param1, param2, param3); // bind需要先绑定,再执行

2.介绍一下defineProperty,hasOwnProperty,isEnumerable

Object.defineProperty(obj,prop,descriptor) 用来给对象定义属性,有 value , writeable , enumerable , set/get , configurable

hasOwnProperty 用于检查某一属性是不是存在于对象本身,

isEnumerable 用来检测某一属性是否可遍历,也就是能不能用 for...in 循环来取到。

3.JS常用设计模式的实现思路(单例、工厂、代理、装饰、观察者模式等)

// 1) 单例: 任意对象都是单例,无须特别处理
    var obj = {name: 'michaelqin', age: 30};

 // 2) 工厂: 就是同样形式参数返回不同的实例
    function Person() { this.name = 'Person1'; }
    function Animal() { this.name = 'Animal1'; }

    function Factory() {}
    Factory.prototype.getInstance = function(className) {
        return eval('new ' + className + '()');
    }

    var factory = new Factory();
    var obj1 = factory.getInstance('Person');
    var obj2 = factory.getInstance('Animal');
    console.log(obj1.name); // Person1
    console.log(obj2.name); // Animal1

 // 3) 代理: 就是新建个类调用老类的接口,包一下
    function Person() { }
    Person.prototype.sayName = function() { console.log('michaelqin'); }
    Person.prototype.sayAge = function() { console.log(30); }

    function PersonProxy() { 
        this.person = new Person();
        var that = this;
        this.callMethod = function(functionName) {
            console.log('before proxy:', functionName);
            that.person[functionName](); // 代理
            console.log('after proxy:', functionName);
        }
    }

    var pp = new PersonProxy();
    pp.callMethod('sayName'); // 代理调用Person的方法sayName()
    pp.callMethod('sayAge'); // 代理调用Person的方法sayAge() 

  // 4) 观察者: 就是事件模式,比如按钮的onclick这样的应用.
    function Publisher() {
        this.listeners = [];
    }
    Publisher.prototype = {
        'addListener': function(listener) {
            this.listeners.push(listener);
        },

        'removeListener': function(listener) {
            delete this.listeners[listener];
        },

        'notify': function(obj) {
            for(var i = 0; i < this.listeners.length; i++) {
                var listener = this.listeners[i];
                if (typeof listener !== 'undefined') {
                    listener.process(obj);
                }
            }
        }
    }; // 发布者

    function Subscriber() {

    }
    Subscriber.prototype = {
        'process': function(obj) {
            console.log(obj);
        }
    }; // 订阅者

    var publisher = new Publisher();
    publisher.addListener(new Subscriber());
    publisher.addListener(new Subscriber());
    publisher.notify({name: 'michaelqin', ageo: 30}); // 发布一个对象到所有订阅者
    publisher.notify('2 subscribers will both perform process'); // 发布一个字符串到所有订阅者

3.处理字符串常用的十个函数

charAt()   // 返回在指定位置的字符。
concat()   // 连接字符串。
fromCharCode()   // 从字符编码创建一个字符串。
indexOf()  // 检索字符串。
match()   // 找到一个或多个正则表达式的匹配。
replace()   // 替换与正则表达式匹配的子串。
search()   // 检索与正则表达式相匹配的值。
slice()   // 提取字符串的片断,并在新的字符串中返回被提取的部分。
split()   // 把字符串分割为字符串数组。
substr()   // 从起始索引号提取字符串中指定数目的字符。
substring()   // 提取字符串中两个指定的索引号之间的字符。
toLocaleLowerCase()   // 把字符串转换为小写。
toLocaleUpperCase()   // 把字符串转换为大写。
toLowerCase()   // 把字符串转换为小写。
toUpperCase()   // 把字符串转换为大写。
toString()   // 返回字符串。
valueOf()   // 返回某个字符串对象的原始值。

4.如何判断一个变量是对象还是数组

function isObjArr(variable){
     if (Object.prototype.toString.call(value) === "[object Array]") {
            console.log('value是数组');
       }else if(Object.prototype.toString.call(value)==='[object Object]'){//这个方法兼容性好一点
            console.log('value是对象');
      }else{
          console.log('value不是数组也不是对象')
      }
}

// 注意:千万不能使用typeof来判断对象和数组,因为这两种类型都会返回"object"。

5.ES5的继承和ES6的继承有什么区别?

ES5 的继承是通过 prototype 或构造函数机制来实现。

ES5 的继承实质上是先创建子类的实例对象,然后再将父类的方法添加到 this 上( Parent.apply(this) )。

ES6 的继承机制实质上是先创建父类的实例对象 this (所以必须先调用父类的 super() 方法),然后再用子类的构造函数修改 this 。具体为 ES6 通过 class 关键字定义类,里面有构造方法,类之间通过 extends 关键字实现继承。子类必须在 constructor 方法中调用 super 方法,否则新建实例报错。因为子类没有自己的 this 对象,而是继承了父类的 this 对象,然后对其调用。如果不调用 super 方法,子类得不到 this 对象。

注意:super关键字指代父类的实例,即父类的this对象。在子类构造函数中,调用super后,才可使用this关键字,否则报错。

6.下面的ul,如何点击每一列的时候alert其index?(闭包)

<ul id="test">
 <li>这是第一条</li>
 <li>这是第二条</li>
 <li>这是第三条</li>
 </ul>


// 方法一:
var lis=document.getElementById('test').getElementsByTagName('li');
for(var i=0;i<3;i++)
{
lis[i].index=i;
lis[i].onclick=function(){
alert(this.index);
};
}

//方法二:
var lis=document.getElementById('test').getElementsByTagName('li');
for(var i=0;i<3;i++)
{
lis[i].index=i;
lis[i].onclick=(function(a){
return function() {
alert(a);
}
})(i);
}

7.对于MVVM的理解

MVVMModel-View-ViewModel 的缩写。

Model 代表数据模型,也可以在 Model 中定义数据修改和操作的业务逻辑。

View 代表 UI 组件,它负责将数据模型转化成 UI 展现出来。

ViewModel 监听模型数据的改变和控制视图行为、处理用户交互,

简单理解就是一个同步 ViewModel 的对象,连接 ModelView

MVVM 架构下, ViewModel 之间并没有直接的联系,

而是通过 ViewModel 进行交互, ModelViewModel 之间的交互是双向的,

因此 View 数据的变化会同步到 Model 中,而 Model 数据的变化也会立即反应到 View 上。

ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,

ViewModel 之间的同步工作完全是自动的,无需人为干涉,

因此开发者只需关注业务逻辑,不需要手动操作 DOM ,

不需要关注数据状态的同步问题,

复杂的数据状态维护完全由 MVVM 来统一管理。

8.解释Vue的生命周期

Vue 实例从创建到销毁的过程,就是生命周期。从开始创建、初始化数据、编译模板、挂载Dom->渲染、更新->渲染、销毁等一系列过程,称之为 Vue 的生命周期。

Vue 的生命周期包括:

beforeCreate (创建前)在数据观测和初始化事件还未开始,

created (创建后)完成数据观测,属性和方法的运算,初始化事件, $el 属性还没有显示出来;

beforeMount( 载入前)在挂载开始之前被调用,相关的 render 函数首次被调用,实例已完成以下的配置:编译模板,把 data 里面的数据和模板生成 html ,注意此时还没有挂载 html 到页面上;

mounted (载入后)在 el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用,实例已完成以下配置:用上面编译好的 html 内容替换 el 属性指向的 DOM 对象,完成模板中的 html 渲染到 html 页面中,此过程中进行 ajax 交互。

beforeUpdate (更新前)在数据更新之前调用,发生在虚拟 DOM 重新渲染和打补丁之前,可以在该钩子中进一步地更改状态,不会触发附加的重渲染过程。

updated (更新后)在由于数据更改导致的虚拟 DOM 重新渲染和打补丁之后调用。调用时,组件 DOM 已经更新,所以可以执行依赖于 DOM 的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环,该钩子在服务器渲染期间不被调用。

beforeDestroy (销毁前)在实例销毁之前调用,实例仍然完全可用。

destroyed (销毁后)在实例销毁之后调用。调用后,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。

9.为什么使用Node.js,它有哪些优缺点?

优点:

事件驱动,通过闭包很容易实现客户端的生命活期。

不用担心多线程,锁,并行计算的问题

V8 引擎速度非常快

对于游戏来说,写一遍游戏逻辑代码,前端后端通用

缺点:

node.js 更新很快,可能会出现版本兼容

node.js 还不算成熟,还没有大制作

node.js 不像其他的服务器,对于不同的链接,不支持进程和线程操作

10.什么是错误优先的回调函数?

错误优先( Error-first )的回调函数( Error-First Callback )用于同时返回错误和数据。

第一个参数返回错误,并且验证它是否出错;其他参数返回数据。

fs.readFile(filePath, function(err, data)
{
    if (err)
    {
        // 处理错误
        return console.log(err);
    }
    console.log(data);
});

11.使用npm有哪些好处?

通过 npm ,你可以安装和管理项目的依赖,

并且能够指明依赖项的具体版本号。

对于 Node 应用开发而言,

可以通过 package.json 文档来管理项目信息,

配置脚本,以及指明依赖的具体版本。

12.在JavaScript源文档的开头包含 use strict 有什么意义和好处?

use strict 是一种在 JavaScript 代码运行时自动实行更严格解析和错误处理的方法。(严格模式)

将值分配给一个未声明的变量会自动创建该名称的全局变量。这是 JavaScript 中最常见的错误之一。在严格模式下,这样做的话会抛出错误。消除 this 强制。

当检测到对象(例如, var object = {foo: "bar", foo: "baz"}; )中重复命名的属性,或检测到函数中(例如, function foo(val1, val2, val1){} )重复命名的参数时,严格模式会抛出错误,因此捕捉几乎可以肯定是代码中的 bug 可以避免浪费大量的跟踪时间。比 eval() 更安全。

13.vuejs与angularjs以及react的区别?

AngularJS 的区别

相同点:

都支持指令:内置指令和自定义指令。

都支持过滤器:内置过滤器和自定义过滤器。

都支持双向数据绑定。

都不支持低端浏览器。

不同点:

1. AngularJS 的学习成本高,比如增加了 Dependency Injection 特性,而 Vue.js 本身提供的 API 都比较简单、直观。

2.在性能上, AngularJS 依赖对数据做脏检查,所以 Watcher 越多越慢。

Vue.js 使用基于依赖追踪的观察并且使用异步队列更新。所有的数据都是独立触发的。

对于庞大的应用来说,这个优化差异还是比较明显的。

React 的区别

相同点:

React 采用特殊的 JSX 语法, Vue.js 在组件开发中也推崇编写 .vue 特殊文档格式,对文档内容都有一些约定,两者都需要编译后使用。

中心思想相同:一切都是组件,组件实例之间可以嵌套。

都提供合理的钩子函数,可以让开发者定制化地去处理需求。

都不内置列数 AJAXRoute 等功能到核心包,而是以插件的方式加载。

在组件开发中都支持 mixins 的特性。

不同点:

React 依赖 Virtual DOM ,而 Vue.js 使用的是 DOM 模板。 React 采用的 Virtual DOM 会对渲染出来的结果做脏检查。

Vue.js 在模板中提供了指令,过滤器等,可以非常方便,快捷地操作 Virtual DOM

14.标签keep-alive的作用是什么?

<keep-alive></keep-alive> 包裹动态组件时,会缓存不活动的组件实例,主要用于保留组件状态或避免重新渲染。

15.WeakMap 和 Map 的区别?

WeakMap 结构与 Map 结构基本类似,唯一的区别是它只接受对象作为键名( null 除外),不接受其他类型的值作为键名,而且键名所指向的对象,不计入垃圾回收机制。

WeakMap 最大的好处是可以避免内存泄漏。一个仅被 WeakMap 作为 key 而引用的对象,会被垃圾回收器回收掉。

WeakMap 拥有和 Map 类似的 set(key, value)get(key)has(key)delete(key)clear() 方法, 没有任何与迭代有关的属性和方法。

16.http和https的基本概念?

http : 超文本传输协议,是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准( TCP ),用于从 WWW 服务器传输超文本到本地浏览器的传输协议,它可以使浏览器更加高效,使网络传输减少。

https : 是以安全为目标的 HTTP 信道,简单讲是 HTTP 的安全版,即 HTTP 下加入 SSL 层, HTTPS 的安全基础是 SSL ,因此加密的详细内容就需要 SSL

https 协议的主要作用是:创建一个信息安全信道,来确保数组的传输,确保网站的真实性。

17.git fetch和git pull的区别?

git pull :相当于是从远程获取最新版本并 merge 到本地

git fetch :相当于是从远程获取最新版本到本地,不会自动 merge

18.介绍一下对浏览器内核的理解?

主要分成两部分:渲染引擎( layout engineerRendering Engine )和 JS 引擎。

渲染引擎:负责取得网页的内容( HTMLXML 、图像等等)、

整理讯息(例如加入 CSS 等),以及计算网页的显示方式,然后会输出至显示器或打印机。

浏览器的内核的不同对于网页的语法解释会有不同,所以渲染的效果也不相同。

所有网页浏览器、电子邮件客户端以及其它需要编辑、显示网络内容的应用进程都需要内核。

JS 引擎:解析和执行 javascript 来实现网页的动态效果。

最开始渲染引擎和 JS 引擎并没有区分的很明确,后来 JS 引擎越来越独立,内核就倾向于只指渲染引擎。

19.什么是微格式

微格式( Microformats )是一种让机器可读的语义化 XHTML 词汇的集合,是结构化数据的开放标准。

是为特殊应用而制定的特殊格式

优点:将智能数据添加到网页上,让网站内容在搜索引擎结果界面可以显示额外的提示。

20.数据绑定基本的实现

// 实现一个方法,可以给 obj 所有的属性添加动态绑定事件,当属性值发生变化时会触发事件
let obj = {
  key_1: 1,
  key_2: 2
}
function func(key) {
  console.log(key + ' 的值发生改变:' + this[key]);
}
bindData(obj, func);
obj.key_1 = 2; // 此时自动输出 "key_1 的值发生改变:2"
obj.key_2 = 1; // 此时自动输出 "key_2 的值发生改变:1"

答案:

function bindData(obj, fn) {
  for (let key in obj) {
    Object.defineProperty(obj, key, {
      set(newVal) {
        if (this.value !== newVal) {
          this.value = newVal;
          fn.call(obj, key);
        }
      },
      get() {
        return this.value;
      }
    })
  }
}

20.数据结构处理

// 有一个祖先树状 json 对象,当一个人有一个儿子的时候,其 child 为其儿子对象,如果有多个儿子,child 为儿子对象的数组。

请实现一个函数,找出这个家族中所有有多个儿子的人的名字(name),输出一个数组。

列子:
// 样例数据
let data = {
  name: 'jack',
  child: [
    { name: 'jack1' },
    {
      name: 'jack2',
      child: [{
        name: 'jack2-1',
        child: { name: 'jack2-1-1' }
      }, {
        name: 'jack2-2'
      }]
    },
    {
      name: 'jack3',
      child: { name: 'jack3-1' }
    }
  ]
}


// 答案:

// 用递归
function findMultiChildPerson(data) {
  let nameList = [];

  function tmp(data) {
    if (data.hasOwnProperty('child')) {
      if (Array.isArray(data.child)) {
        nameList.push(data.name);
        data.child.forEach(child => tmp(child));
      } else {
        tmp(data.child);
      }
    }
  }
  tmp(data);
  return nameList;
}

// 不用递归
function findMultiChildPerson(data) {
  let list = [data];
  let nameList = [];

  while (list.length > 0) {
    const obj = list.shift();
    if (obj.hasOwnProperty('child')) {
      if (Array.isArray(obj.child)) {
        nameList.push(obj.name);
        list = list.concat(obj.child);
      } else {
        list.push(obj.child);
      }
    }
  }
  return nameList;
}

刷前端面经笔记(八)
刷前端面经笔记(八)