闭包
闭包就是函数能够记忆住当初定义时候的作用域,不管函数到哪里执行了,永远都能够 记住那个作用域,并且会遮蔽新作用域的变量。可预测状态容器;实现模块化,实现变量的私有封装;可以实现迭代器。 闭包缺点:1.闭包有一个非常严重的问题,那就是内存浪费问题,这个内存浪费不仅仅 因为它常驻内存,更重要的是,对闭包的使用不当的话会造成无效内存的产生;2.性能问题 使用闭包时,会涉及到跨作用域访问,每次访问都会导致性能损失。 因此在脚本中,最好小心使用闭包,它同时会涉及到内存和速度问题。不过我们可以通过把跨作用域变量存储在局部变量中,然后直接访问局部变量,来减轻对执行速度的影响。
原型和原型链
原型:每一个对象类型都有一个隐式原型__ proto __ ,每一个函数都有一个显示原型prototype,该属性指向它的原型对象。
原型链:某个对象的原型又有自己的原型,直到某个对象的原型为null为止,组成这条的最后一环,这种一级一级的链就是原型链。
递归和递归优化
递归就是函数自己调用自己。但是又不能无限的调用自己,需要有一个出口,否则会成为死循环。函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。
// 循环求1-5的所有数的和
var sum = 0;
for(var i = 1; i <= 5; i++){
sum += i;
}
console.log(sum) // 15
//递归实现1-5的所有数的和
function sum(n){
if(n === 1){
return 1;
}
return n + sum(n-1);
}
console.log(sum(5)); //15
尾递归优化是解决递归调用栈溢出的方法。尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。
// 上例递归进行尾递归优化
function sum(n, m = 0){
if(n === 1){
return 1 + m;
}
return sum(n-1, n + m);
}
console.log(sum(5)); //15
// 或者while优化
function sum(n, m = 0){
while(n >= 1){
return sum(n - 1, n + m);
}
return m;
}
console.log(sum(5)); // 15
ajax工作原理和封装
1.创建XMLHttpRequest对象。 2.设置请求方式。open() 3.调用回调函数。onreadystatechange 4.发送请求。send()
function ajax(options) {
const { type, dataType, data, timeout, url, success, error } = options;
var params = formatParams(data);
var xhr;
//考虑兼容性
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else if (window.ActiveObject) {
//兼容IE6以下版本
xhr = new ActiveXobject("Microsoft.XMLHTTP");
}
//启动并发送一个请求
if (type == "GET") {
xhr.open("GET", url + "?" + params, true);
xhr.send();
} else if (type == "POST") {
xhr.open("post", url, true);
//设置表单提交时的内容类型
//Content‐type数据请求的格式
xhr.setRequestHeader(
"Content‐type",
"application/x‐www‐form‐urlencoded"
);
xhr.send(params);
}
// 设置有效时间
setTimeout(function () {
if (xhr.readySate != 4) {
xhr.abort();
}
}, timeout);
// 接收
// options.success成功之后的回调函数 options.error失败后的回调函数
//xhr.responseText,xhr.responseXML 获得字符串形式的响应数据或者XML形式的响应数据
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
var status = xhr.status;
if ((status >= 200 && status < 300) || status == 304) {
success && success(xhr.responseText, xhr.responseXML);
} else {
error && error(status);
}
}
};
}
//格式化请求参数
function formatParams(data) {
var arr = [];
for (var name in data) {
arr.push(
encodeURIComponent(name) + "=" + encodeURIComponent(data[name])
);
}
arr.push(("v=" + Math.random()).replace(".", ""));
return arr.join("&");
}
跨域
跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的。其 实我们通常所说的跨域是狭义的,是由浏览器同源策略限制的一类请求场景。同源策略SOP(Same origin policy)是一种约定,由Netscape公司1995年引入浏览器,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR 等攻击。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地 址,也非同源。
方法1:跨域资源共享CORS跨域,就是服务端在HTTP返回头上加上“AccessControll-Allow-Origin:*”。 “Access-Controll-Allow-METHODS:GET, POST” DELETE、PATCH请求类型会发出OPTIONS预检请求。
方法2:代理跨域,webpack-dev-server里面的proxy配置项。config中的 ProxyTable
方法3:JSONP,利用页面srcipt没有跨域限制的漏洞,用script的src引入它,然后页 面内定义回调函数,jQuery中$.ajax({dataType: ‘jsonp’})。
方法4: iframe跨域,配合window.name或者 location.hash或者document.domain 一起使用
方法5:nginx反向代理接口跨域,通过nginx配置一个代理服务器(域名与domain1 相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中 domain信息,方便当前域cookie写入,实现跨域登录。
方法6:jquery的ajax跨域,dataType:'jsonp'