angular变化检测

angular变化检测

ChangeDetectionStrategy

源码 描述使用哪种变化检测器。

  • OnPush = 0 OnPush 检测一次CheckOnce(只有当@Input改变了,才进行变化检测并render。)
  • Default = 1 Default 总是检测CheckAlways

ChangeDetectorStatus

源码 描述监测器的状态

  • CheckOnce = 0 意味着在执行变化检测后,状态将变成 Checked
  • Checked = 1 该状态下,变化检测总是跳过,除非它的状态变成CheckOnce
  • CheckAlways = 2 总是执行变化检测,执行后状态还是CheckAlways
  • Detached = 3 表示检测器子树不是主树的一部分,应该跳过。应该是调用ChangeDetectorRef.detach。
  • Errored = 4 意味着变化检测器遇到了一个错误,状态将变成Errored。此状态中的更改检测器将不再检测更改。
  • Destroyed = 5 意味着变化检测器已经销毁。

NgZone

api:NgZone

ngZone包装了大量的事件和异步函数,下面的函数执行完成,会自动告诉Angular去执行变化监测。当发生变化时则重新渲染。

NgZone有3个方法runOutsideAngular``runGuarded``run类型都是(fn: () => any): any;。

  1. runOutsideAngular就是在fn中执行上面的方法,不会通知angular进行变化检测。
  2. runGuarded``run差不多,fn执行后进行变化检测。run会重复抛出错误且不会上报onError。runGuarded则相反。runGuarded应该更安全一点。

ChangeDetectorRef

api:ChangeDetectorRef

class ChangeDetectorRef {
  markForCheck(): void
  detach(): void
  detectChanges(): void
  checkNoChanges(): void
  reattach(): void
}
  • markForCheck() - 从自己到根组件检测器为OnPush的组件, 设置状态为CheckOnce
  • detach() - 从变化检测树中分离,该组件将不再执行变化检测,除非手动调用 reattach() 方法。
  • reattach() - 重新添加已分离的变化检测器。
  • detectChanges() - 从该组件到各个子组件执行一次变化检测,一般是配合detach使用,会触发 ApplicationRef.tick()方法
  • checkNoChanges() - 从该组件到各个子组件执行一次变化检测,若发生改变则抛出检测,一般用于debug。

fetch 带cookie跨域访问

fetch

今天,新项目,以前是自己用nginx反向代理,来解决开发跨越的问题。现在,后台设置Header Access-Control-Allow-Origin:*,在fetch上,遇到了一点困难。

export async function get_companies(search = '') {
  return request(`/companies/`, {
    method: "get",
    credentials: 'include',
    mode: 'cors',
    headers: {
       'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
    }
  });
}

直接这样请求。会报错

Fetch API cannot load http://dadigua.com/companies/. The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. Origin 'http://localhost:4000' is therefore not allowed access.

是因为后台只设置了Header Access-Control-Allow-Origin:*,此时可以进行跨越访问了。但是还能不带cookie,把credentials: 'include',注释,就成功了。

要想带cookie,后台必须还要设置。response.setHeader("Access-Control-Allow-Credentials","true");

ajax写法

  • 后台设置Access-Control-Allow-Credentials: true;

  • 前端var xhr = new XMLHttpRequest();xhr.withCredentials = true;

js---Event Loop

js是一门单线程语言。但不是简单的单线程,它还有异步任务机制。js引擎同步代码全部在 “执行栈” 跑,除了这个还有一个”任务队列”(task queue)。一旦”执行栈”中的所有同步任务执行完毕,系统就会读取”任务队列”,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入下一个执行栈,开始执行。整个过程是无限循环的,这就是Event Loop(事件循环)。

定时器

在浏览器中,除了dom和网络异步任务外,剩下的就是定时器了。setTimeout()和setInterval(),HTML5标准规定了setTimeout()的第二个参数的最小值(最短间隔),不得低于4毫秒,如果低于这个值,就会自动增加。老版本的浏览器都将最短间隔设为10毫秒。因此,setTimeout(()=>{},0),根本不是0秒。

Node.js的Event Loop

node除了setTimeout和setInterval这两个方法,还有process.nextTick和setImmediate。

process.nextTick方法可以push到当前”执行栈”的尾部。setImmediate方法则是在当前”任务队列”的尾部添加事件,是在下一次Event Loop时执行。

process.nextTick(function A() {
  console.log(1);
  process.nextTick(function B(){console.log(2);});
});

setTimeout(function timeout() {
  console.log('Next Event Loop');
}, 0)
// 1
// 2
// TIMEOUT FIRED

setTimeout和setImmediate这两个一样,都是在下一次Event Loop时执行。他们的先后顺序是不确定的。

在使用递归的时候,应该谨慎使用process.nextTick。因为process.nextTick总是在执行,主线程不会去读取”事件队列”!

ES6代理实现对象监控

以前写过一个es6代理的博客,但是只简单讲了。可以当对象监控使用。但是在下面这样的情况,就无效了

比如一个对象obj={name:{first:’小’,last:’明’}},把obj设置set代理,假如我这样obj.name.last=’红’,并不会触发set。这样不是我们要结果,当我们改变obj时,我可以知道对象改变了。下面利用闭包保存变量实现。

实现

function runProxy(state,key='this') {
    if (typeof state != 'object'||state==null) {
        return state
    }
    let prototype={}
    for (let x in state) {
            prototype[x]=runProxy(state[x],key+'.'+x);
    }
    return new Proxy(state, {
        set(target, property, value, receiver) {
            let  oldValue=target[property]
            let  newValue=value
            console.log("change property:%s --- %coldValue:%o ---%c> newValue:%o",key+'.'+property.toString(),"color:red",oldValue,"color:green",newValue)
            prototype[property]=runProxy(value,key+'.'+property.toString());
            return Reflect.set(target, property, value);
        },
        get(target, property){
            return Reflect.get(prototype, property);
        }
    })
}

运行如下代码, 如图。在ngDva中把state监控。触发一个action,改变了state中什么属性,全部打印到控制台了。

obj={name:{first:'小',last:'明'}}
let Pobj=runProxy(obj)
Pobj.name.first='刘'
Pobj.name.last='红'
Pobj.name.first={};
Pobj.name.first.hhh='大地瓜'
Pobj.name.first=[];
Pobj.name.first[1]='大地瓜'

最近发现上面的方法还是有bug,如下代码,obj.name最后变成了代理对象。

obj={name:{first:'小',last:'明'}}
let Pobj=runProxy(obj)
Pobj.name.first='刘'
Pobj.name.last='红'
Pobj.name.first={};
Pobj.name=Pobj.name.first

最终版:

function runProxy(state, key = 'this', callback=()=>{}) {
    if (typeof state != 'object' || state == null || Array.isArray(state)) {
        return state
    }
    let prototype = {}
    prototype['getgetgetgetgetgetgetgetgetgetgetget'] = function () {
        return state;
    }
    for (let x in state) {

        prototype[x] = runProxy(state[x], key + '.' + x, callback);

    }
    let p = new Proxy(state, {
        set(target, property, value, receiver) {

            let oldValue = target[property]


            let newValue = value;

            try {
                if (typeof newValue['getgetgetgetgetgetgetgetgetgetgetget'] == "function") {
                    newValue = newValue['getgetgetgetgetgetgetgetgetgetgetget']();
                }

            } catch (e) {
                console.log(e)
            }

            callback(key + '.' + property.toString(), oldValue, newValue)
            // console.log("change property:%s --- %coldValue:%o ---%c> newValue:%o", key + '.' + property.toString(), "color:red", oldValue, "color:green", newValue)

            prototype[property] = runProxy(value, key + '.' + property.toString(), callback);

            return Reflect.set(target, property, newValue);
        },
        get(target, property) {
            return Reflect.get(prototype, property);
        }
    })

    return p;
}

angular/animations学习

ng这个animations还是蛮好用的。加入场,离场各种效果,还是满酷炫的。写了一个提示组件。是一个队列,依次显示。然后按照定义的时间消失,在提示进场和离场时还有动画。如图:

ng4代码

import { Component, Input, OnInit, } from '@angular/core';
import {
  trigger,
  state,
  style,
  animate,
  transition,keyframes
} from '@angular/animations';
import * as Rx from 'rxjs';
import { Observable, Subject } from 'rxjs';


let list:any = []

var subject = new Rx.BehaviorSubject(list);

let id = 0;

function log(type:string, content:string, duration = 1500, onClose?:any) {
    id++;
    let obj = {
        type: type,
        content,
        id,
        onClose,
        state:'in'
    }
    list = [...list, obj]

    function close() {
        list = list.filter((x:any) => {
            if (x.id == obj.id) {
                if (obj.onClose != null) { obj.onClose(); }

                return false;
            } else { return true; }
        })
        subject.next(list)
    }
    setTimeout(close, duration)
    subject.next(list)
    return close;
}

@Component({
    selector: 'acMessage',
    template: `<div data-reactroot="" class="ant-message">
        <span >
             <ng-container *ngFor="let item of list|async">
                <div [@flyInOut]="'in'" class="ant-message-notice">
                    <div    class="ant-message-notice-content">
                        <div class="ant-message-custom-content ant-message-{{item.type}}" [ngSwitch]="item.type">
                            <i *ngSwitchCase="'success'" class="anticon anticon-check-circle"></i>
                            <i *ngSwitchCase="'error'" class="anticon anticon-cross-circle"></i>
                            <i *ngSwitchCase="'info'" class="anticon anticon-info-circle"></i>
                            <i *ngSwitchCase="'warning'" class="anticon anticon-exclamation-circle"></i>
                            <i *ngSwitchCase="'loading'" class="anticon anticon-spin anticon-loading"></i>

                            <span>{{item.content}}</span>
                        </div>
                    </div>
                </div>
             </ng-container>
        </span>
    </div>`,
    animations: [
        trigger('flyInOut', [
            state('in', style({ opacity: 1, transform: 'translateX(0)' })),
    //        state('done', style({  opacity: 0.5 ,transform: 'translateX(100%)', })),
            transition('void => in', [
                style({
                    opacity: 0,
                    transform: 'translateX(-100px)'
                }),
                animate('0.2s ease-in')
            ]),
            transition('* => void', [
                animate('0.2s  ease-out', style({
                    opacity: 0.5,
                    transform: 'translateX(100px)',
                }))
            ]),

        ])
    ]
})
export default class message {

    list: Observable<any>;



    state: Observable<any>;
    constructor() {

        this.list = subject.asObservable();
    }

    static success(content:any, duration = 1500, onClose?:any) {
        log('success', content, duration, onClose)
    }
    static error(content:any, duration = 1500, onClose?:any) {
        return log('error', content, duration, onClose)
    }
    static info(content:any, duration = 1500, onClose?:any) {
        return log('info', content, duration, onClose)
    }
    static warning(content:any, duration = 1500, onClose?:any) {
        return log('warning', content, duration, onClose)
    }
    static warn(content:any, duration = 1500, onClose?:any) {
        return log('warning', content, duration, onClose)
    }
    static loading(content:any, duration = 1500, onClose?:any) {
        return log('loading', content, duration, onClose)
    }
}

ng2动画是从‘@angular/core’引入。

import { Component, Input, OnInit, trigger, state, animate, transition, style } from '@angular/core';

ngDva

最近在ng4上面用redux,感觉redux原生的Reducer写法还是太麻烦了。写了一个function生成。还加入co用于 Generator 函数的自动执行和全局store修改。还有日志功能,输出action.type,action.payload,当前Reducer的参数即model的状态,全局的store(用于修改其它Reducer/model中的状态)。

import co from 'co';
import * as warning from 'warning';

const SEP = '/'
function run(model: any, store, log) {
    const { namespace, effects } = model;
    warning(namespace != null, `namespace is a must`);
    warning(effects != null, `effects is a must`);
    function applyNamespace(type) {
        function getNamespacedReducers(reducers) {
            return Object.keys(reducers).reduce((memo, key) => {
                warning(
                    key.indexOf(`${namespace}${SEP}`) !== 0,
                    `app.model: ${type.slice(0, -1)} ${key} should not be prefixed with namespace ${namespace}`,
                );
                memo[`${namespace}${SEP}${key}`] = reducers[key];
                return memo;
            }, {});
        }

        if (model[type]) {
            if (type === 'effects') {

                model[type] = getNamespacedReducers(model[type]);

            }
        }
    }

    applyNamespace('effects');
    Object.defineProperty(model, 'namespace', { enumerable: false, writable: false, configurable: false })
    Object.defineProperty(model, 'effects', { enumerable: false, writable: false, configurable: false })

    // for(var x in model){
    //     debugger;
    // }
    function res(state = model.state, action) {

        for (var key in model.effects) {
            if (action.type == key) {
                co(model.effects[key].bind(state, action, store)).then((val) => {
                    if (action.callback != null) {
                        action.callback(val)
                    }
                    if (process.env.node_ENV != 'production') {
                        if (log) {
                            console.group("dispatch log");
                            console.log("action type:",action.type);
                            console.log("action payload:",action.payload);
                            console.log("current state:",state);
                            console.log("global store:",store);
                            console.groupEnd();
                        }
                    }
                }, (e) => { console.error(e.stack); })

                return state
            }
        }
        return state;
    }
    return res
}

function dva(models, log = false) {

    let store = {};
    let root = {}
    models.forEach((x) => {

        root[x.namespace] = x.state;
        store[x.namespace] = run(x, root, log);
    });
    return store;
}
export default dva;

使用:

const data = {
    namespace: 'data',

    state: {
        cout:0
    },

    effects: {
        *INCREMENT({payload}) {
            var data=yield new Promise(function(resolve, reject) {
                    setTimeout(()=>{
                        resolve(2)
                    },1000)
                });

            this.cout += data;
        },
        *DECREMENT({payload},store) {
            this.cout -= 1;
         //   console.log(store);
         //   setTimeout(()=>{store.data.state+=1;},1000)
         //   store.data.state-=1;
        },
        *RESET({payload}) {
            this.cout = 0;
        }
    }
}

StoreModule.provideStore(dva([data],true)),

Rxjs学习

我们在用canvas画图的时候。一般实现

var isDown=false;
dom.addEventListen('mousedown',function(){
     isDown=true;
},false)
dom.addEventListen('mouseup',function(){
     isDown=false;
},false)
dom.addEventListen('mousemove',function(){
     if(isDown){
     //xxxxxx
     }
},false)

最近发现一个Rxjs的库。实现很巧妙。

const down$ = Rx.Observable.fromEvent(dom , 'mousedown').map(()=>'down')
const up$ = Rx.Observable.fromEvent(dom, 'mouseup').map(() => 'up')
var move$ = Rx.Observable.fromEvent(dom, 'mousemove')

const upAndDown$ = up$.merge(down$)
upAndDown$.switchMap(action => 
            action==='down'?move$:Rx.Observable.empty() 
).subscribe(value => {
    console.log(value)
})

不过我也要mousedown按下的事件,稍微改下代码就行,实现如下:

const down$ = Rx.Observable.fromEvent(dom , 'mousedown')
const up$ = Rx.Observable.fromEvent(dom , 'mouseup').map(() => 'up')
var move$ = Rx.Observable.fromEvent(dom, 'mousemove')

const upAndDown$ = up$.merge(down$)
upAndDown$.switchMap(action => {
    if (action != 'up') {
        return move$.startWith(action);
    } else {return Rx.Observable.empty() }
}).subscribe(value => {
    console.log(value)
})

Minimum Height Trees--LeetCode

问题:给定一个拥有树性质的无向图,图的每一个节点都可以视为一棵树的根节点。在所有可能的树中,找出高度最小的树,并返回他们的树根。

Given n = 6, edges = [[0, 3], [1, 3], [2, 3], [4, 3], [5, 4]]
0  1  2
 \ | /
   3
   |
   4
   |
   5
return [3, 4]

这个是无向图,建立邻接矩阵,使用Floyd算法,算出每个点到任意的点的距离,也就是树高度。 结果Time Limit Exceeded,仔细想下,Floyd算法会把每个点到任意的点的距离都求出来,这是没必要的。Dijkstra算法+减枝,应该更快。

#define Infinity 99999999
class Solution {
public:
    vector findMinHeightTrees(int n, vector>& edges) {
        vector> V;
        for (int i = 0;i < n; ++i) {
            vector vec(n, Infinity);
            vec[i] = 0;
            V.push_back(vec);
        }
        for (int i = 0;i < edges.size();++i) {
            V[edges[i].first][edges[i].second] = 1;
            V[edges[i].second][edges[i].first] = 1;
        }
        for (int i = 0;i < n;++i)
            for (int j = 0;j < n;j++)
                for (int k = 0;k < n;k++)
                    if (V[j][k]>V[j][i] + V[i][k])
                        V[j][k] = V[j][i] + V[i][k];

        int Min = INT_MAX;
        vector res;
        for (int i = 0;i < n;++i) {
            int Max = 0;
            for (int j = 0;j < n;++j) {
                if (Max < V[i][j]) {
                    Max = V[i][j];
                }
            }
            //        printf("%d\n", Max);
            if (Min > Max) {
                res.clear();
                res.push_back(i);
                Min = Max;
            }
            else {
                if (Min == Max) {
                    res.push_back(i);
                }
            }

        }
        return res;
    }
};

面试准备+复习

功能:停止事件冒泡

event.stopPropagation( ); // W3C,阻止事件冒泡

event.cancelBubble = true; //在IE下

功能:阻止事件默认行为

event.preventDefault();// W3C addEventListener

event.returnValue = false; //在IE下

return false;//通过on这种方式的绑定的,使用return false;

浏览器缓存控制机制

https://juejin.im/entry/5ad86c16f265da505a77dca4

  • Expires
  • Cache-control:max-age策略
  • Last-Modified/If-Modified-Since
  • Etag

AMD / CMD

AMD 是 RequireJS 在推广过程中对模块定义的规范化产出。AMD 是提前执行,依赖前置。

define(['./a', './b'], function(a, b) {  // 依赖必须一开始就写好
    a.doSomething()
    // 此处略去 100 行
    b.doSomething()
    ...
}) 

CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。CMD 是延迟执行,依赖就近。

define(function(require, exports, module) {
   var a = require('./a')
   a.doSomething()   // 此处略去 100 行
   var b = require('./b') // 依赖可以就近书写
   b.doSomething()   // ... 
})

js加载

默认情况javascript是同步加载的,也就是javascript的加载时阻塞的。因此js加载要放后最后。也可以加上defer/async,或者动态创建script标签。前端图片等资源,是等html下载完成后,发起多线程请求加载。

dom渲染

  • 浏览器会解析三个东西:一个是HTML/SVG/XHTML,事实上,Webkit有三个C++的类对应这三类文档。解析这三种文件会产生一个DOM Tree。CSS,解析CSS会产生CSS规则树。Javascript,脚本,主要是通过DOM API和CSSOM API来操作DOM Tree和CSS Rule Tree.
  • 解析完成后,浏览器引擎会通过DOM Tree 和 CSS Rule Tree 来构造 Rendering Tree。注意:Rendering Tree 渲染树并不等同于DOM树,因为一些像Header或display:none的东西就没必要放在渲染树中了。CSS 的 Rule Tree主要是为了完成匹配并把CSS Rule附加上Rendering Tree上的每个Element。也就是DOM结点。也就是所谓的Frame。然后,计算每个Frame(也就是每个Element)的位置,这又叫layout和reflow过程。
  • 最后通过调用操作系统Native GUI的API绘制。转自知乎
  • 因此,要把css放head中。

Reflow 和 Repaint

Repaint「重绘」改变 DOM 元素的视觉效果,如颜色,透明度,隐藏(visibility),outline 。一般自会递归自身和子元素。

Reflow「回流」,不仅会改变自己,还会影响别的元素。因此在要尽量避免这样的开销。比如一个元素大小改变了,一般会影响别的元素的布局。如display。

减少回流
  • 不要用 inline style 或 table 布局;
  • 如果想设定元素的样式,通过改变元素的 class 名 (尽可能在 DOM 树的最里层);
  • 用于表现动画的元素,使用 position 属性的 fixed 值或 absolute 值(脱离文档流);
  • 减少不必要的 DOM 层级;
  • 避免设置多项内联样;;

HTTP协议 “无连接,无状态”

无连接

  • HTTP/1.0 指的是每次连接只处理一个请求,服务端处理完客户端一次请求,等到客户端作出回应之后便断开连接;这种方式有利于节省服务器资源.明显,HTTP1.0,会在建立和断开连接上花费大部分时间;
  • HTTP/1.1 Keep-Alive提出来解决上面的问题,且持久连接称为了默认的连接方式。但是线端阻塞(head-of-line blocking), 它是指一个连接(connection)一次只提交一个请求的效率比较高, 多了就会变慢。 HTTP/1.1 试过用流水线(pipelining)来解决这个问题, 但是效果并不理想(数据量较大或者速度较慢的响应, 会阻碍排在他后面的请求).
  • HTTP/2是完全多路复用的,而非有序并阻塞的——只需一个连接即可实现并行

无状态

  • 无状态是指协议对于事务处理没有记忆能力,服务器不知道客户端是什么状态。即我们给服务器发送 HTTP 请求之后,服务器根据请求,会给我们发送数据过来,但是,发送完,不会记录任何信息。这样明显是不行的。Cookie+Session应运而生。

HTTP 2.0与HTTP 1.1区别

  • HTTP/2采用二进制格式而非文本格式
  • HTTP/2是完全多路复用的,而非有序并阻塞的——只需一个连接即可实现并行
  • 使用报头压缩,HTTP/2降低了开销
  • HTTP/2让服务器可以将响应主动“推送”到客户端缓存中

X-UA-Compatible

指定IE=edge来指示IE使用它支持的最高模式。

添加http头add_header “X-UA-Compatible” “IE=Edge”;

<meta http-equiv="X-UA-Compatible" content="IE=edge">

Google PageSpeed

Google提供了 PageSpeed工具,这是一个浏览器插件,可以很好地应用上文中Google所提到的Web优化实践——帮助你轻松对网站的性能瓶颈进行分析,并为你提供优化建议。

301与302的区别

301 redirect: 301 代表永久性转移(Permanently Moved)。

302 redirect: 302 代表暂时性转移(Temporarily Moved )。

  • 对用户来说没有区别,他们看到效果只是一个跳转
  • 对于引擎及站长:302转向可能会有URL规范化及网址劫持的问题。可能被搜索引擎判为可疑转向,甚至认为是作弊。

字符串拼接

在Javascript中使用”+”号来拼接字符串效率是比较低的,因为每次运行都会开辟新的内存并生成新的字符串变量,然后将拼接结果赋值给新变量。与之相比更为高效的做法是使用数组的join方法,即将需要拼接的字符串放在数组中最后调用其join方法得到结果。

css关系选择符

  • E F 选择所有被E元素包含的F元素。
  • E>F 选择所有作为E元素的子元素F。
  • E+F 选择紧贴在E元素之后F元素。
  • E~F 选择E元素后面的所有兄弟元素F。

事件触发器

  • fireEvent IE

  • dispatchEvent 其他高级浏览器

    var fireEvent = function(element,event){

    if (document.createEventObject){  
        // IE浏览器支持fireEvent方法  
        var evt = document.createEventObject();  
        return element.fireEvent('on'+event,evt)  
    }  
    else{  
        // 其他标准浏览器使用dispatchEvent方法  
        var evt = document.createEvent( 'HTMLEvents' );  
        evt.initEvent(event, true, true);  
        return !element.dispatchEvent(evt);  
    }  

    };

事件监听/取消

  • attachevent detachEvent IE下事件名称+on
  • addeventlistener removeEventListener

Javascript内存泄露

  • 循环引用 一个 DOM 对象被一个 Javascript 对象引用,与此同时又引用同一个或其它的 Javascript 对象,这个 DOM 对象可能会引发内存泄露。IE 6, 7中有一部分对象并不是原生js对象。例如,其DOM和BOM中的对象就是使用C++以COM对象的形式实现的,而COM对象的垃圾回收机制采用的就是引用计数策略。这样就会触发循环引用,引起内存泄露
  • Javascript闭包
  • DOM插入

JavaScript 垃圾回收机制

  • 引用计数法 早期IE采用的。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1。如果同一个值又被赋给另一个变量,则该值的引用次数加1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减1。当这个值的引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。这样,当垃圾回收器下次再运行时,它就会释放那些引用次数为0的值所占用的内存。
  • 标记清除法 当变量进入环境时,例如,在函数中声明一个变量,就将这个变量标记为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为“离开环境”。更新的更好的方法。

text-overflow: clip|ellipsis|string;

clip 修剪文本。    
ellipsis 显示省略符号来代表被修剪的文本。
string 使用给定的字符串来代表被修剪的文本。

whiteSpace: normal|pre|nowrap|pre-wrap|pre-line|inherit

normal    默认。空白会被浏览器忽略。
pre    空白会被浏览器保留。其行为方式类似 HTML 中的 <pre> 标签。
nowrap    文本不会换行,文本会在在同一行上继续,直到遇到 <br> 标签为止。
pre-wrap    保留空白符序列,但是正常地进行换行。
pre-line    合并空白符序列,但是保留换行符。
inherit    规定应该从父元素继承 white-space 属性的值。

box-sizing content-box|border-box

  • content-box W3C盒模型
  • border-box 和IE盒模型一样(新的盒模型)

link和@import区别

  • link属于XHTML标签,除了加载CSS外,还能用于定义RSS, 定义rel连接属性等作用;而@import是CSS提供的,只能用于加载CSS;
  • 页面被加载的时,link会同时被加载,而@import引用的CSS会等到页面被加载完再加载;
  • import是CSS2.1 提出的,只在IE5以上才能被识别,而link是XHTML标签,无兼容问题;

HTML5为什么只需要写?

Html5不基于SGML,因此不需要对DTD进行引用,但是需要doctype来规范浏览器的行为(让浏览器按照他们应该的方式来运行)而HTML4.01基于SGML,所以需要对DTD进行引用,才能告知浏览器文档所使用的文档类型。

Margin垂直塌陷(collapse)问题

折叠的产生情况:

1.必须是处于常规文档流(非float和绝对定位)的块级盒子,并且处于同一个BFC当中。

2.没有线盒,没有空隙(clearance,下面会讲到),没有padding和border将他们分隔开(ps

:所以解决办法中有padding或者border两种办法)

3.都属于垂直方向上相邻的外边距,

解决办法:

加border|padding|overflow:hidden

对BFC规范(块级格式化上下文:block formatting context)的理解?

BFC (Block Formatting Contexts) 即块级格式化上下文,从样式上看,它与普通的容器没有什么区别,但是从功能上,BFC 可以看作是隔离了的独立容器,容器里面的元素不会在布局上影响到外面的元素,并且 BFC 具有普通容器没有的一些特性,例如可以包含浮动元素,(如 overflow 方法)就是触发了父元素的 BFC ,使到它可以包含浮动元素,从而防止出现高度塌陷的问题。(在IE6-7中没有BFC,但是有hasLayout和BFC有相似的功能,*zoom: 1 触发 hasLayout)

移动端最小触控区域是多大?

MIT的一项研究指出,大多数成年人的食指宽度为16-20mm,换算后大约为45-57px。Apple的44px*44px操作起来就很舒适了。

页面编码和被请求的资源编码(如js)如果不一致如何处理?

http://www.yyy.com/a.html 中嵌入了一个http://www.xxx.com/test.js
a.html 的编码是gbk或gb2312的。 而引入的js编码为utf-8的 ,那就需要在引入的时候
<script src="http://www.xxx.com/test.js" charset="utf-8"></script>
同理,如果你的页面是utf-8的,引入的js是gbk的,那么就需要加上charset="gbk".

javascript里面的继承怎么实现,如何避免原型链上面的对象共享

利用原型来继承,通过增加一个空的函数来避免原型链上的对象共享

function Cat(name){  
Animal.call(this);  
this.name = name || 'Tom';
}
(function(){  // 创建一个没有实例方法的类  
var Super = function(){};  
Super.prototype = Animal.prototype;  
//将实例作为子类的原型  Cat.prototype = new Super();
})();

如何保证一致性的代码风格

JSLint|JSHint|ESLint|JSCS。在团队开发中,这些工具对于编写代码非常的有帮助,能够帮助团队开发者强制执行规定的风格指南,还能够通过静态分析捕获常见的错误。

CSS Hack

IE6:_

IE7:+

IE6 & IE7:*

IE6 & IE7 & IE8 & IE9 & IE10:\9

IE8 & IE9 & IE10:\0

IE9 & IE10:\9\0

createDocumentFragment

createDocumentFragment方法用来创建一个DocumentFragment。在前面我们说到DocumentFragment表示一种轻量级的文档,它的作用主要是存储临时的节点用来准备添加到文档中,解决添加大量节点时的性能问题。

(今天,面试被问了。)

webpack2--devServer

用dev-server开发时代理,决解开发时跨域问题

包装一下fetch,让url前面自动加上 '/test'。devServer配置,如下。

devServer: {
    contentBase: __dirname + "/src",  // New
    proxy: {
        '/test/*': {
          target: 'http://192.168.99.106:5000',
           pathRewrite: {'^/test' : ''},
           changeOrigin: true,
           secure: false
        }
     }
},

webpack2+antd+andt moblie脚手架

自己实验一下webpack2的新功能。还是和1有点不同,很多配置改变了。

前端工程目录

webpack.config.js

var path = require("path")
module.exports = {
    context: __dirname + "/src",
    entry: {
        index: ["babel-polyfill", "./index.js"],
    },
    output: {
        filename: "[name].bundle.js",
        path: __dirname + "/dist/",

        publicPath: "/",            // New
    },
    module: {
        rules: [{
            test: /\.jsx?$/,
            use: [{
                loader: "babel-loader",
                options: {
                    presets: ["react", "es2015", "stage-0"],
                    plugins: ["transform-runtime",]
                },
            }],
            exclude: /node_modules/

        }, {
            test: /\.scss$/,
            use: [{
                loader: "style-loader" // creates style nodes from JS strings
            }, {
                loader: "css-loader" // translates CSS into CommonJS
            }, {
                loader: "sass-loader" // compiles Sass to CSS
            }]
        }, {
            test: /\.css$/,
            use: [{
                loader: "style-loader" // creates style nodes from JS strings
            }, {
                loader: "css-loader" // translates CSS into CommonJS
            }]
        }, {
            test: /\.(svg)$/i,
            use: [{
                loader: 'svg-sprite-loader'
            }],
            include: [
                require.resolve('antd-mobile').replace(/warn\.js$/, ''),
                path.resolve(__dirname, 'src/svgs')
            ]
        }]
    },
    resolve: {
        extensions: ['.web.js', '.js', '.json'],
        modules: ['node_modules', path.resolve(__dirname, 'node_modules')]
    },
    devServer: {
        contentBase: __dirname + "/src",  // New
    },
};

package.json

{
  "name": "myapp",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "./node_modules/.bin/webpack-dev-server",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "webpack2"
  ],
  "author": "ldh",
  "license": "ISC",
  "devDependencies": {
    "babel-core": "^6.24.0",
    "babel-loader": "^6.4.1",
    "babel-plugin-import": "^1.1.1",
    "babel-plugin-transform-runtime": "^6.23.0",
    "babel-preset-es2015": "^6.24.0",
    "babel-preset-react": "^6.23.0",
    "babel-preset-stage-0": "^6.22.0",
    "css-loader": "^0.27.3",
    "node-sass": "^4.5.0",
    "sass-loader": "^6.0.3",
    "style-loader": "^0.14.1",
    "svg-sprite-loader": "^0.3.0",
    "webpack": "^2.2.1",
    "webpack-dev-server": "^2.4.2"
  },
  "dependencies": {
    "antd": "^2.8.1",
    "antd-mobile": "^1.0.6",
    "babel-polyfill": "^6.23.0",
    "codemirror": "^5.24.2",
    "dva": "^1.2.1",
    "react": "^15.4.2",
    "react-dom": "^15.4.2"
  }
}

.babelrc

{"plugins": [["import", { "style": "css", "libraryName": "antd-mobile" }]]}

单链表反序。

这是一道常见的笔试题,leetcode上有两道题。Reverse Linked List和Reverse Linked List II。

Reverse Linked List

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(head==NULL)  {return head;}
        ListNode *p1,*p2,*p3;

        p1 = head;
        p2 = head->next;
        while (p2!=NULL)
        {
            p3 = p2->next;
            p2->next = p1;
            p1 = p2;
            p2 = p3;
        }
        head->next = NULL;
        head = p1;
        return p1;
    }
};

Reverse Linked List II 反序从m至n区间中的链表。

class Solution {
public:
    ListNode* reverseBetween(ListNode* head, int m, int n) {
        int index = 1;
        ListNode *temp = head;
        ListNode *temp2 = temp;
        bool is = false;
        while (index<m) {
            is = true;
            temp2 = temp;
            temp = temp->next;
            index++;
        }
        ListNode *p1, *p2, *p3;

        p1 = temp;
        p2 = temp->next;

        while (p2 != NULL&&index<n) {
            p3 = p2->next;
            p2->next = p1;
            p1 = p2;
            p2 = p3;
            index++;
        }
        temp->next = p2;
        if (is) {
            temp2->next = p1;
            return head;
        }
        else {
            return p1;
        }
    }
};

Convert Sorted List to Binary Search Tree--leetcode

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
//中序递归
class Solution {
public:
    TreeNode* sortedListToBST(ListNode* head) {
        int len = 0;
        ListNode* temp = head;
        while (temp != NULL) {
            len++;
            temp = temp->next;
        }
        TreeNode* tree = buildTree(head, len);
        return tree;
    }
    TreeNode* buildTree(ListNode* &head, int len) {
        TreeNode* root;
        if (len val);
        head = head->next;
        root->left = left;
        root->right = buildTree(head, len - mid - 1);
        return root;
    }
};

Diameter of Binary Tree--leetcode

解题思路

求2叉树的两节点的最长路径,可以不经过根节点。老套路递归,返回当前节点的左右子树的最长路径的解(res不经过当前节点),一条打算经过当前节点的最长路径,比较右边最大的解,左边最大的解,以及左右和起来的最大的解。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */

 struct Node{
     int res;
     int max;
     Node(int res,int max):res(res),max(max){}
 };
class Solution {
public:
    int diameterOfBinaryTree(TreeNode* root) {
        if(root= = NULL){return 0;}
        Node n=run(root);
        return n.res-1;
    }
    Node run(TreeNode* root){
        if(root==NULL){
            return Node(0,0);
        }
        Node a=run(root->left);
        Node b=run(root->right);
        return Node(max(max(a.res,b.res),a.max+b.max+1),max(a.max+1,b.max+1));
    }
};

px2rem-vc code插件推荐

px2rem

Sass(使用Sass的函数、混合宏这些功能来实现):

$root-font-size:100px;

@mixin px2rem($property,$px-values,$baseline-px:$root-font-size,$support-for-ie:false){
  $baseline-rem: $baseline-px / 1rem * 1;
  @if $support-for-ie {
    #{$property}: $px-values;
  }
  @if type-of($px-values) == "number"{
    #{$property}: $px-values / $baseline-rem;
  }
  @else {
    $rem-values:();
    @each $value in $px-values{

      @if $value == 0 or type-of($value) == "number"{
        $rem-values: append($rem-values, $value / $baseline-rem);
      }
    }
    #{$property}: $rem-values;
  }
}
//funtion写法
@function Px2rem($px-values,$baseline-px:$root-font-size){
    $baseline-rem: $baseline-px / 1rem * 1;
    @if type-of($px-values) == "number"{
        @return  $px-values / $baseline-rem;
    }
    @else {
        $rem-values:();
        @each $value in $px-values{
            @if $value == 0 or type-of($value) == "number"{
                $rem-values: append($rem-values, $value / $baseline-rem);
            }
        }
        @return  $rem-values;
    }
}

px2rem-0.2.0.vsix

这一个vs code下的px2rem插件

scss去单位

 @function strip-units($number){
    @return $number / ($number * 0 + 1);
}

Unique Binary Search Trees II--leetcode

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
var m=make(map[string][]*TreeNode)
func createTree(start,end int)[]*TreeNode {
    elem,ok:=m[string(start)+string(end)]
    if(ok){
        return elem
    }
    res:=[]*TreeNode{}
    if start>end {  
        res=append(res,nil)
        return res;
    }  
    for i:=start;i<=end;i++{
         lefts:=createTree(start,i-1)
         rights:=createTree(i+1,end)
         for j:=0;j<len(lefts);j++{
             for k:=0;k<len(rights);k++{
                 var temp=TreeNode{i,nil,nil}
                 temp.Left=lefts[j]
                 temp.Right=rights[k]
                 res=append(res,&temp)
             }
         }
    } 
    m[string(start)+string(end)]=res
    return res
}

func generateTrees(n int) []*TreeNode {
    if(n<=0){
        res:=[]*TreeNode{}
        return res
    }else{
        return createTree(1,n)
    }

}

极光推送-点击推送通知唤醒应用android

jpush:1.5.2

react-native:0.4.2

是因为应用挂在后台后,mModule变成了null。

if (mModule != null && mModule.mContext != null) {
    Intent intent = new Intent();
    intent.setClass(context, mModule.mContext.getClass());
    intent.putExtras(bundle);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
    context.startActivity(intent);                    
} else {
    Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(mRAC.getPackageName());//mRAC.getPackageName()==你的应用包名
   launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);                    
   launchIntent.putExtras(bundle);
   context.startActivity(launchIntent);                    
}

上面的代码在收到通知,点击通知后, 就会创建1个activity,可能导致要多退出一次才能退出成功。

修改AndroidManifest.xml,添加android:launchMode=”singleTask”。

<activity
     android:name=".MainActivity"
     android:label="@string/app_name"
     android:launchMode="singleTask"  

React-Native 安卓下极光推送

1.安装踩坑

按照官方教程来有一处坑。

在修改app/src…/MainActivity.java中,教程漏了点东西。提示这个错误。

:app:compileDebugJavaWithJavac - is not incremental (e.g. outputs have changed, no pre
C:\Users\ldh\WebstormProjects\RN\AwesomeProject\android\app\src\main\java\com\myapp\Ma
    protected void onCreate(Bundle savedInstanceState) {
                            ^
  符号:   类 Bundle
  位置: 类 MainActivity
C:\Users\ldh\WebstormProjects\RN\AwesomeProject\android\app\src\main\java\com\myapp\Ma
        JPushInterface.init(this);
        ^
  符号:   变量 JPushInterface
  位置: 类 MainActivity
C:\Users\ldh\WebstormProjects\RN\AwesomeProject\android\app\src\main\java\com\myapp\Ma
        JPushInterface.onPause(this);
        ^
  符号:   变量 JPushInterface
  位置: 类 MainActivity
C:\Users\ldh\WebstormProjects\RN\AwesomeProject\android\app\src\main\java\com\myapp\Ma
        JPushInterface.onResume(this);
        ^
  符号:   变量 JPushInterface
  位置: 类 MainActivity
4 个错误
:app:compileDebugJavaWithJavac FAILED

明显是JPushInterface和Bundle未定义。只需加上这两句.即可

import cn.jpush.android.api.JPushInterface;
import android.os.Bundle;

使用jpush

        import JPushModule from 'jpush-react-native';
        JPushModule.addReceiveCustomMsgListener((message) => {
          //   console.warn("addReceiveCustomMsgListener" +JSON.stringify(message));
          //  自定义消息
        });
        JPushModule.addReceiveNotificationListener((message) => {
          //  console.warn("receive notification: " + JSON.stringify(message));
          //  通知消息
        })
         JPushModule.addReceiveOpenNotificationListener((map) => {
           // 点击通知后触发的事件
           //  console.warn("addReceiveOpenNotificationListener: " + JSON.stringify(map));
                this.props.navigator.push({
                    name: 'page2'
                })
          })
其他
function RunPush(data) {
  AsyncStorage.getItem('userInfo').then((result) => {
    if (result != JSON.stringify(data.data)) {
      AsyncStorage.setItem('userInfo', JSON.stringify(data.data))
      JPushModule.setAlias(data.data.userName, () => { }, () => { }) //设置别名
      if( data.data.isDeveloper===1){
        JPushModule.setTags(["developer"], () => {}, () => { }); //设置标签
      }else{
        JPushModule.setTags(["user"], () => {}, () => { });
      }

    } else {

    }
  })
}

nginx反向代理,获取客户端ip地址

web服务器如nodejs获取客户端ip地址。一般使用req.connection.remoteAddress。但是当我们使用了nginx反向代理时候,这时req.connection.remoteAddress永远就是本机ip。因为web服务器3000端口永远是80端口代理过来的,可以把Nginx配置改下,可以解决。

server {
    listen 80 ;
    server_name xss.dadigua.win;
    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $host;
    }
}

这时只需获取http头x-real-ip即可。如nodejs,req.headers[‘x-real-ip’]。