针对:https://www.nowcoder.com/discuss/377224

1、判断一个对象是否是数组

参考:https://www.cnblogs.com/ma-shuai/p/7805264.html

  • typeof不行,typeof判断别的方法都可以,但是判断array和null都会判断为object
  • instanceof检测对象的原型链是否指向构造函数的prototype对象的,但是instanceof是不能用于有不同框架的环境。
    var arr = [1,2,3,1];
    alert(arr instanceof Array); // true 
  • 对象的constructor属性
    var arr = [1,2,3,1];
    alert(arr.constructor === Array); // true 
    但上述方法是有漏洞的,当你在多个frame中来回穿梭的时候,这两种方法就亚历山大了。由于每个iframe都有一套自己的执行环境,跨frame实例化的对象彼此是不共享原型链的,因此导致上述检测代码失效

无懈可击的方法

  • Object.prototype.toString
    首先,取得对象的一个内部属性[[Class]],然后依据这个属性,返回一个类似于"[object Array]"的字符串作为结果(看过ECMA标准的应该都知道,[[]]用来表示语言内部用到的、外部不可直接访问的属性,称为“内部属性”)。利用这 个方法,再配合call,我们可以取得任何对象的内部属性[[Class]],然后把类型检测转化为字符串比较,以达到我们的目的。
    function isArrayFn (o) {
    return Object.prototype.toString.call(o) === '[object Array]';
    }
    var arr = [1,2,3,1];
    alert(isArrayFn(arr));// true 
  • Array.isArray()

2、css 三角形

使用border属性

.trangle{
  width: 0px;
  height: 0px;
  border: 100px solid #000;
  border-top-color: red;
  border-bottom-color: transparent;
  border-left-color: transparent;
  border-right-color: transparent;
}
<div class="trangle"></div>

3、翻转一个单向链表

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
            val(x), next(NULL) {
    }
};*/
class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) {
        //把a指向b改成b指向a


        //例子a->b->c->d
        //pre=NULL
        //next=null
        //cur=a;
        ListNode* pre=NULL;
        ListNode* next=NULL;
        ListNode* cur=pHead;
        while(cur!=NULL){
            //next=b next=c
            next=cur->next;
            //a,next=null b.next=a
            cur->next=pre;
            if(next==NULL) break;
            //pre=a pre=b
            pre=cur;
            //cur=b cur=c
            cur=next;
        }
        return cur;
    }
};

4、青蛙跳台阶

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

class Solution {
public:
    int jumpFloor(int number) {
        if(number==1) return 1;
        if(number==2) return 2;
        //跳n个台阶的话,从n-1或者n-2台阶开始跳
        //f(n)=f(n-1)+f(n-2)
        //所以是斐波那契
        int a=1;
        int b=2;
        int c=0;
        for(int i=2;i<number;i++){
            c=a+b;
            a=b;
            b=c;
        }
        return c;
    }
};

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

class Solution {
public:
    int jumpFloorII(int number) {
    if(number==1) return 1;
    if(number==2) return 2;
    //跳到n格时f(n)=f(1)+f(2)+...+f(n-1)+1
    //跳到n-1格时,f(n-1)=f(1)+....+f(n-2)+1
    //相减f(n)=f(n-1)*2
     return jumpFloorII(number-1)*2;
    }
};

5、箭头函数和普通函数的区别

1、箭头函数中本身没有this,函数内部的this都是外部环境中的this
2、箭头函数没有自己的arguments对象,但是可以访问外围函数的arguments对象
3、不能通过new调用,也没有new.target值和原型

6、谈一谈nginx你都怎么用

1、反向代理----最常用

反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。简单来说就是真实的服务器不能直接被外部网络访问,所以需要一台代理服务器,而代理服务器能被外部网络访问的同时又跟真实服务器在同一个网络环境,当然也可能是同一台服务器,端口不同而已。

2、负载均衡

——HTTP服务器(动静分离)

——正向代理

7、Http缓存

1、判断是否过期-》强缓存/2
2、 if-None-Match到浏览器,判断是否和ETag相同-》协商缓存/3
3、if-Not-Defined
强缓存:expires和cache-control
协商缓存:ETag和if-none-Match

8.性能优化扩展开讲

https://www.cnblogs.com/tianshu/p/10555921.html

1、垃圾收集

日常中的某些情况下垃圾收集器无法回收无用变量,导致的一个结果就是——内存使用率不断增高,以下为对应的情况以及处理方法。

①对象相互引用会导致引用计数始终为2,所以用完对象后应将引用设为null

let element = document.getElementById("test");
let myObject = new Object();
myObject.element = element;
element.someObject = myObject;

//....用完后需要加如下代码
myObject.element = null;
element.someObject = null;

②当数据不再有用时,需要通过将值设为null来解除引用,该做法适用于大多数全局变量和全局对象属性

function createPerson(name){
    let localPerson = new Object();
    localPerson.name = name;
    return localPerson
}

let globalPerson = createPerson("test")

//...用完后手动解除
globalPerson = null

③关于与闭包相关的内存泄漏

function assignHandler(){
  let element = document.getElementById("test");
  element.onclick = function(){
    alert(element.id)    
  }          
}

//以上会导致element的引用数无法被回收,更改如下
function assignHandler(){
  let element = document.getElementById("test");
  let id = element.id;

  element.onclick = function(){
    alert(id)
  }          
  element = null;  
}

2、事件委托

在js中,添加到页面上的事件处理程序数量会直接关系到页面整体运行运行性能。导致这一问题的原因是多方面的。

首先函数都是对象,都会占用内存;内存中对象越多,性能就越差。

其次,必须事先指定所有事件处理程序而导致的DOM访问次数,会延迟整个页面的交互就绪时间。

1、同类型的事件处理函数过多时,应该结合为一个

2、内存留有过时不用的“空事件处理程序”

//例子中id为myBtn的点击事件变为了空事件处理程序
<div id="myDiv">
    <input type="button" value="Click Me" id="myBtn">
</div>
<script type="text/javascript">
    let btn = document.getElementById("myBtn");
    btn.onclick = function(){
      document.getElementById("myDiv").innerHTML = "xxxx";  
    };
</script>

//改善点即需要手工移除事件处理程序
<div id="myDiv">
    <input type="button" value="Click Me" id="myBtn">
</div>
<script type="text/javascript">
    let btn = document.getElementById("myBtn");
    btn.onclick = function(){
      btn.onclick = null;
      document.getElementById("myDiv").innerHTML = "xxxx";  
    };
</script>

这也是造成性能问题的主因,两种情况下会造成该问题。
运用removeChild()和replaceChild()方法去除节点时;
在使用innerHTML替换页面某一部分时,如果带有事件处理程序的元素被innerHTML删除了,那么原有事件处理函数极有可能无法被回收
//例子中id为myBtn的点击事件变为了空事件处理程序//因为这个innerHTML覆盖了这个事件,原本是点击事件,现在变成了xxx//改善点即需要手工移除事件处理程序 //设置点击事件为空

3、注意作用域

访问全局变量会比访问局部变量要慢

1、若某处循环使用全局变量时,我们可以略做修改

2、尽量少用with,因为with会增加其中执行代码的作用域链的长度

4、使用正确的方法

要了解各种算法的复杂度,选择合适的算法

9.async await

async关键字用于异步编程,用它声明的函数返回值是一个promise对象
await关键字只能使用在async函数中
当async函数运行过程中,遇到await会停止,等到await后的异步函数运行完成之后再继续运行

10.await 发生异常了还能不能继续往下之行 try/catch捕获

async/await在错误处理方面,主要使用try/catch

// 定时器
function timer(params) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            // resolve('resolve');
            reject('reject');
        }, params);
    })
}

// async/await
async function fn(params) {
    try {
        var value = await timer(params);
    } catch (error) {
        console.log(error);        
    }
}

fn(1000); //执行

promise的状态变为 rejected 之后会跳转到 catch

11.一个需求 Promise 串行

输入一个数组,将里面的函数异步执行
异步执行任务A、B、C
但js是同步执行任务的
方法:

1、reduce方法

1、使用数组的reduce方法,reduce里有四个参数,pre,next,index,arr,
2、如果then方法里返回的是一个promise对象,那么执行下一个then 的时候必定是在上一个then执行完之后执行

var createPromise = function(time) {
    return (resolve, reject)=>
    new Promise((resolve, reject)=>{
        setTimeout(()=>{
            console.log('timein'+time)
            resolve();
        }, time*1000)
    })
}

function serpromise(arr) {
    arr.reduce((pre, next, index, carr)=>{
       //传进去的next是每一个数组的当前元素,相当于现在传进去的是time
        return pre.then(next)
    }, Promise.resolve())
}

var arr=[createPromise(2),createPromise(1),createPromise(3),createPromise(4),createPromise(5)];
// Promise.resolve().then(createPromise(2)).then(createPromise(1))
serpromise(arr)

例子:

function delay(time) {
  return new Promise((resolve, reject) => {
    console.log(`wait ${time}s`)
    setTimeout(() => {
      console.log('execute');
      resolve()
    }, time * 1000)
  })
}

const arr = [3, 4, 5];

图片说明

arr.reduce((s, v) => {
  //v是currentvalue,s是必需的。初始值, 或者计算结束后的返回值。
  return s.then(() => delay(v))
}, Promise.resolve())

2、async + 循环 + await

(
  async function () {
    for (const v of arr) {
      await delay(v)
    }
  }
)()

3、普通循环

let p = Promise.resolve()
for (const i of arr) {
  p = p.then(() => delay(i))
}

4、递归

function dispatch(i, p = Promise.resolve()) {
  if (!arr[i]) return Promise.resolve()
  return p.then(() => dispatch(i + 1, delay(arr[i])))
}
dispatch(0)

5、for await of

function createAsyncIterable(arr) {
  return {
    [Symbol.asyncIterator]() {
      return {
        i: 0,
        next() {
          if (this.i < arr.length) {
            return delay(arr[this.i]).then(() => ({ value: this.i++, done: false }));
          }

          return Promise.resolve({ done: true });
        }
      };
    }
  }
}

(async function () {
  for await (i of createAsyncIterable(arr)) { }
})();

6、generator

function* gen() {
  for (const v of arr) {
    yield delay(v)
  }
}

function run(gen) {
  const g = gen()

  function next(data) {
    const result = g.next(data)
    if (result.done) return result.value
    result.value.then(function(data) {
      next(data)
    })
  }

  next()
}

run(gen)

12.箭头函数可以使用 arguments吗 使用对象结构const arrow = (...args) => { console.log(args) }

在使用箭头函数时,arguments 指向的对象并不是当前函数所属的argments,而是上级函数的arguments,所以需要将箭头函数转为function。

这一点与箭头函数中的this相像。

13.For of 和 for in 的区别

参考:https://www.jianshu.com/p/c43f418d6bf0

遍历数组

用for,也可以用forEach,map,filtere,some,every,reduce,reduceRight.
但是使用forEach遍历数组的话,使用break不能中断循环,使用return也不能返回到外层函数

for in遍历的是数组的索引(键名),而for of遍历的是数组元素值
for of遍历的只是数组内的元素,而不包括数组的原型属性method和索引name

for in的问题

1.index索引为字符串型数字,不能直接进行几何运算
2.遍历顺序有可能不是按照实际数组的内部顺序
3.使用for in会遍历数组所有的可枚举属性,包括原型
所以for in更适合遍历对象,不要使用for in遍历数组。

那么除了使用for循环,如何更简单的正确的遍历数组达到我们的期望呢(即不遍历method和name),ES6中的for of更胜一筹.

总结

  • for..of适用遍历数/数组对象/字符串/map/set等拥有迭代器对象的集合.但是不能遍历对象,因为没有迭代器对象.与forEach()不同的是,它可以正确响应break、continue和return语句
  • for-of循环不支持普通对象,但如果你想迭代一个对象的属性,你可以用for-in循环(这也是它的本职工作)或内建的Object.keys()方法: