日常笔记

这边的笔记主要记载自己看周刊或者不错的文章中的一些good points,或者一些需要注意的点,也加入了自己的一些想法 :)

1.const var let区别

  • let const的作用域更小,为块作用域({}),var为函数作用域。如果是for循环中的let ,如

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    var x = 0;
    for (let i = 0; i < 10; i++) {
    x += 10;
    //i只在该作用域存在
    }
    try {
    console.log(i);
    } catch(e) {
    console.log(
    'i does not exist here!'
    );
    }

    输出为: ‘i does not exist here!’

  • const只能在声明时初始化,它表示它所代表的引用(可以认为是指针)是不可变的,所指向的内容实际是可以变的。如

    1
    2
    3
    4
    5
    const a = {};
    a['b'] = 1;
    const b = [];
    b.push(1)
  • 三者都会变量提升(hoist),只是var提升时会默认设置初始值 undefined,而let,const不会。
    因此,如果在声明时没有初始化,然后就去访问值的话,var会报出undefined,而let,const会抛出ReferenceError

    1
    2
    3
    4
    //类似于介个样子
    var a;
    console.log("a",a); //undefined
    a = 3;

2. this 总结

最近写ife的task的时候遇到这么一个问题,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Queue.prototype = {
...
bubbleSort:function(){
var swap = false,that = this,firstRound = true;
var arr = this.queue,
i = 1,
lastIndexOfNotSortedArr = arr.length-1;
var bubbleSortId = setInterval(_runSort,interval);
function _runSort(){
...
this.render();
//这边的this实际上是window,而不是new出来的对象
}
}

记得 你不知道的javascript里面有讲解过,好不容易翻出来一点,回忆一下。

  • a. 全局/function中的this都指向window对象
  • b. 显示调用如 a.func(),或者用构造函数新建对象b = new B()时,里面的this 分别指向a,b
  • c. apply,call,bind指向绑定的对象
  • d. 内部函数中的this指向window 此处就印证了上面的代码中的问题

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    还有这样的栗子
    function Foo() {
    this.value = 42;
    this.method = function() {
    // this refers to the global object
    console.log(this.value); // undefined
    console.log(this === window); // true
    };
    setTimeout(this.method, 500);
    }
补充栗子

除此之外,还有一个栗子需要特别注意,对照着看b,d两条。

See the Pen this in event by lu (@luchen) on CodePen.

1
2
3
4
5
6
7
8
9
10
11
12
13
var element = document.getElementById('element');
var user = {
firstname: 'Bob',
greeting: function(){
alert('My name is ' + this.firstname);
}
};
user.greeting(); // attention1: My name is Bob
// Attach user.greeting as a callback
element.addEventListener('click', user.greeting);
// attention2: My name is undefined

attention1处符合b的规则,而attention2中,则实际上是因为:事件绑定函数时,会以当前元素为作用域执行。
(虽然楼主最开始以为是把user.greeting函数赋给一个匿名回调函数,function(){} = user.greeting.因此在触发它的时候this其实是丢失了的)。不过测试了下,貌似确实是把this绑在了元素上的。具体看codepen里面this.nodeName。
关于这个问题的解决方案有两种:

  • 包裹在另一个匿名函数中

    1
    element.addEventListener('click', function(){user.greeting()});
  • bind user

    1
    2
    3
    4
    element.addEventListener('click', user.greeting.bind(user));
    //或者一步到位:
    user.greeting = user.greeting.bind(user);
    element.addEventListener('click', user.greeting);

—————— 这是一条栗子分割线 end —————

具体的解决方案有三种:

  • var that = this
  • bind
  • es6箭头函数,实际上也是用的第一种方法(经babel转译后可以看到,里面是没有this的,是沿用的外部的this)
    具体见bi项目开发总结第26条

===更新
上面我们说到过元素的事件中this是与元素绑定的,那么如果在事件函数中另外定义的回调函数会丢失this.

1
2
3
4
5
6
document.getElementById('div').onclick = function(){
console.log(this.id); //div
var func = function(){
console.log(this.id) //undefined
}
}

如果

var getId = document.getElementById
然后执行 getId('div')。会实际上变成调用普通函数getElementById,this就指向了windows,而不是document.因此需要进行绑定document。

1
2
3
4
5
document.getElementById = (
return function(func){
return func.apply(document,arguments)
}
)(document.getElementById);

3.中文正则

1
/\u4E00-\u9FA5/

4.跨域总结

在公司开发的过程中其实也遇到过这个问题,是像另一个服务器post请求,是的,坑有两个,一个是跨域,一个是跨域没法用post =。=。但是当时时间紧迫,后端同学不是很会配置,就粗暴的都用jsonp+get解决了

4.1 场景

同源策略,出于防范跨站脚本的攻击,禁止客户端脚本(如 JavaScript)对不同域的服务进行跨站调用。跨站调用主要指,只要网站的

  • 协议名protocol
  • 主机host
  • 端口号port

这三个中的任意一个不同,网站间的数据请求与传输(ajax请求)便构成了跨域调用。
而用script标签请求和解析则可以正常运作。

有一点需要注意的是,跨域请求并非是浏览器限制了发起跨站请求,而是请求可以正常发起,到达服务器端,但是服务器返回的结果会被浏览器拦截。

更直白一点的解释就是,以钓鱼网站(get服务器数据)为例。ta0bao.com,用户提交信息,那么黑客会发送ajax到taobao.com,然后taobao那边的服务器返回数据,如果没有同源策略的话,这个数据就会被获取到。但是有了同源策略,浏览器那边可以得到这个数据,但是黑客在代码里是得不到的。被浏览器拦截了,就不给你就不给你=。=。

同源只能限制不被获取数据,但是发送其实是限制不了的。比如获取用户的私密信息。比如xss。黑客可以在用户提交信息(加上现在在a.com下)时发送两个ajax,一个给正常的网站(a.com),一个给自己的服务器(b.com)。这个是米有办法阻拦哒,要不然钓鱼网站也不会辣么猖狂…摊手。

这实际上也说明了跨域出现的原因及解决方案(用获取script代替ajax请求)

4.2 解决方案:JSONP

JSONP是 JSON 的一种使用模式,可以解决主流浏览器的跨域数据访问问题。其原理是根据 XmlHttpRequest 对象受到同源策略的影响,而 script 标签元素却不受同源策略影响,可以加载跨域服务器上的脚本,网页可以从其他来源动态产生 JSON 资料。用 JSONP 获取的不是 JSON 数据,而是可以直接运行的 JavaScript 语句。
json数据被封装在回调函数里,就是所谓的json with padding(json填充)

我们通常会直接用jquery的jsonp的解决方案,用法如下:

1
2
3
4
5
6
7
8
9
requestDict["update"] = $.ajax({
url: urls.update+encodeURIComponent(JSON.stringify(queriesObj)),
type: "get",
dataType: "jsonp",
jsonp: "callback",
jsonpCallback: "handleServerPolyData",//回调
success: function(serverData) {
},
);

最终发出的url是这样的

1
http://bigdata.xiaojukeji.com/hotmap/getHeatmapOverlay?productLine=taxi?callback=handleServerHeatData?

但实际上它是做了一层封装,会有点误导。返回的实际上还是javascript语句,jquery进行了解析,正确时会调用success,同时把里面的数据存为上面的serverData。让你看起来jsonp跟普通的ajax请求没有任何区别。

实际上如果自己实现的话,应该是这样的:
server1 请求页面有一个script标签

1
<script src = 'http://localhost:3001/jsonServerResponse?jsonp=jsonpCallback'></script>

或者在代码里手动createElement(“script”),然后设置src后append到head中。

1
2
3
4
//server1请求页面的js代码只有下面的回调
function jsonpCallback(data) {
console.log("jsonpCallback: "+data.name)
}
1
2
3
4
5
6
7
8
9
10
// server2处理请求
app.get('/jsonServerResponse', function(req, res) {
var cb = req.query.jsonp
console.log(cb)
var data = 'var data = {' + 'name: $("#name").val() + " - server 3001 jsonp process",' + 'id: $("#id").val() + " - server 3001 jsonp process"' + '};'
var debug = 'console.log(data);'
var callback = '$("#submit").click(function() {' + data + cb + '(data);' + debug + '});'
res.send(callback)
res.end()
})

可以看出,服务端返回的应该是一段立即执行的js代码。

4.3 优缺点
  • 优点:解析快+解决跨域。jsonp中,由于数据是被当成原生的javascript,因此解析和原生的一样快,所以数据传输时,时间(下载+解析)中的解析部分几乎为0
  • 缺点(动态脚本注入引起的):
    • 不安全
    • 只能get请求
    • 不能设置请求的头信息,也就不能设缓存。也不可以访问请求的头信息
    • 不能失败处理
    • 需要等到所有数据返回才可以访问

针对jsonp不能支持post等请求的问题,解决方案是cors(Cross-Origin Resource Sharing)
主要是服务器对响应头进行了处理

1
2
//Access-Control-Allow-Origin
res.header("Access-Control-Allow-Origin", "*");

reference:前端跨域请求原理及实践

===2016/11/20更新
本次更新是针对jsonp不能post的问题。之前我们说了可以用cors,这个楼主还没有试过。楼主这次是用前端代理的方法,本质是本地请求本地服务器,本地服务器去做转发,服务器间的转发是没有跨域这个概念的。因此可以解决,而这种方法下,服务端实际上不需要像cors那样进行变更。(场景是,本地开发需要访问另一个域名下的数据)。
具体方法是:

  • vue-cli中,是使用了http-proxy-middleware的中间件(node-http-proxy)进行处理,如下
1
2
3
4
5
6
7
proxyTable: {
'/ip/serverInfo': {
target: 'https://cnodejs.org', //origin,域名部分
pathRewrite:{'^/ip/serverInfo': '/api/v1/topics'},
changeOrigin: true
},
},
  • webpack中有专门的配置,其实也是利用的http-proxy-middleware
1
2
3
4
5
6
7
8
9
// In webpack.config.js
{
devServer: {
proxy: {
'/api': {
target: 'https://other-server.example.com',
secure: false
}
}

5. 即时搜索

监听v-model变化,每次变动就取消上一次请求。当超过500ms的时候就发送搜索请求。

1
2
3
4
5
6
7
watch: {
searchInput(newValue, oldValue){
clearTimeout(timer);
if(newValue.trim() !==oldValue.trim())
timer = setTimeout(this.fetchKeywordList,500);
}
},
1
2
3
4
5
6
7
var value = document.cookie.split(/;\s*/)
.map(opt.autoencode ? opt.decode : function (d) { return d; })
.map(function (part) { return part.split('='); })
.reduce(function (parts, part) {
parts[part[0]] = part[1];
return parts;
}, {})[data];

demo:

var arr = [{“id”:3,”zh_name”:”test”},{“id”:5,”zh_name”:””lili}]
结果:Object {3: “test”, 5: “lili”}

1
2
3
4
arr.reduce((prev,cur)=>{
prev[cur["id"]] = cur["zh_name"];
return prev;
},{})