1、call、apply、bind的区别
这三个都是用来定义上下文的,call、apply会指定上下文并执行函数;而bind终身定 死上下文但是不执行函数,并返回新的函数。 其中call和apply传入参数的形式有别,call是单独罗列,逗号隔开参数;apply是数 组。 函数.call(上下文对象,参数,参数,参数); 函数.apply(上下文对象,[参数,参数,参数]);
var obj = {
a: 10
}
function fun(b, c){
console.log(this.a + b + c);
}
fun.call(obj, 3, 4);
fun.apply(obj, [3, 4]);
fun = fun.bind(obj); // 返回新的函数
fun(3,4);
相同点: 作用相同,都是动态修改this指向;都不会修改原先函数的this指向。 不同点 执行方式不同 call和apply是改变后页面加载之后就立即执行,是同步代码。 bind是异步代码,改变后不会立即执行;而是返回一个新的函数 传参方式不同 call和bind传参是一个一个逐一传入,不能使用剩余参数的方式传参。 apply可以使用数组的方式传入的,只要是数组方式就可以使用剩余参数的方式传入。 修改this的性质不同 call、apply只是临时的修改一次,也就是call和apply方法的那一次;当再次调用原函数的时候,它的指向还是原来的指向。 bind是永久修改函数this指向,但是它修改的不是原来的函数;而是返回一个修改过后新的函数,此函数的this永远被改变了,绑定了 就修改不了。
2、数据类型有哪些
基本类型:数字number、字符串string、布尔boolean、undefined、null、symbol
引用类型:数组array、函数function、对象object
3、如何检测数据类型
typeof 能够检测:数字、字符串、布尔、undefined、symbol、function
instanceof 能够检测:数组
Object.prototype.toString.call() 万能法
4、各语句的区别
4.1、for和for...in和for...of的区别
for循环,遍历整个数组
for...in加强循环,不光可以遍历数组,还可以遍历对象和其原型上的方法
for...of遍历数组和可枚举的对象
4.2、switch和if的区别
switch用于判断精准的值
if用于判断值的范围
4.3、while和do...while的区别
while当符合条件时则执行
do...while先执行一次,然后再判断是否符合条件,比while要多执行一次
4.4、break和continue的区别
break是跳出当前循环并终止循环
continue是跳出当前循环并执行下一次循环
5、闭包
闭包就是函数能够记忆住当初定义时候的作用域,不管函数到哪里执行了,永远都能够 记住那个作用域,并且会遮蔽新作用域的变量。可预测状态容器;实现模块化,实现变量的私有封装;可以实现迭代器。 闭包缺点:1.闭包有一个非常严重的问题,那就是内存浪费问题,这个内存浪费不仅仅 因为它常驻内存,更重要的是,对闭包的使用不当的话会造成无效内存的产生;2.性能问题 使用闭包时,会涉及到跨作用域访问,每次访问都会导致性能损失。 因此在脚本中,最好小心使用闭包,它同时会涉及到内存和速度问题。不过我们可以通过把跨作用域变量存储在局部变量中,然后直接访问局部变量,来减轻对执行速度的影响。
function foo(){
var a = 0;
function f(){
a++;
return a;
}
return f;
}
var res = foo();
res();
res();
res();
console.log(res()); // 4 a的值被存储在内存中不会被释放掉
能够访问其他函数内部变量的函数 优点:会延伸变量的使用范围,一直存储在全局变量中 避免全局污染 缺点:会造成内存泄漏,浪费性能
6、原型和原型链
原型:每一个引用类型都有一个隐式原型__ proto __ ,每一个函数都有一个显示原型prototype,该属性指向它的原型对象。
原型链:某个对象的原型又有自己的原型,直到某个对象的原型为null为止,组成这条的最后一环,这种一级一级的链就是原型链。
原型:在函数声明时,会自动创建一个共享空间,用于存储共享的属性和方法 原型链:一种查找机制,先找实例本身,如果找不到在__proto__中查找,没有再去构造函数中查找,都找不到就返回undefined
7、继承
7.1、原型链继承
创建父类和子类的构造函数 让子类继承父类 让子类的prototype = 父类的实例化对象 实例化子类 缺点:不能传参, 创建的对象都是一样的,一改全改
/**
* 缺点:引用类型的属性被所有实例共享,
* 在创建Child 的实例时, 不能向Person传参
*/
function Person() {
this.name = "哈哈";
}
Person.prototype.getName = function () {
console.log(this.name);
};
function Child() {}
Child.prototype = new Person();
7.2、借用构造函数继承(经典继承,对象冒充继承)
创建父类和子类的函数 在子类里面添加父类 使用:父类.call(this,参数...) 优点:可以传参,避免了引用类型的属性被所有实例共享 缺点:获取不到父类的prototypr中的方法,浪费内存
/* 优点: 1.避免了引用类型的属性被所有实例共享 2.可以在Child中向Parent传参 缺点: 1.只是子类的实例,不是父类的实例 2.方法都在构造函数中定义,每次创建实例都会创建一遍方法 */ function Child() { Person.call(this); }
7.3、组合继承
创建父类和子类(使用对象冒充继承,父类.call)的构造函数 让子类的prototype = 父类的实例化对象 缺点:调用了俩次父类的构造函数
/* 优点:融合原型链继承和构造函数的优点,是JavaScript中最常用的继承模式 缺点:调用了两次父类构造函数 组合继承最大的问题是无论什么情况下,都会调用两次超类型构造函数:一次是在创建子 类型原型的时候,另一次是在子类型构造函数内部) */
function Child(name, age) {
Parent.call(this, name); // 第二次调用 Parent()
this.age = age;
}
Child.prototype = new Parent(); // 第一次调用 Parent()
7.4、原型式继承
// 缺点: 包含引用类型的属性值始终都会共享相应的值, 这点跟原型链继承一样
function CreateObj(o) {
function F() {}
F.prototype = o;
return new F();
}
var person = {
name: "xiaopao",
friend: ["daisy", "kelly"],
};
var person1 = CreateObj(person);
7.5、寄生式继承 可以理解为在原型式继承的基础上增加一些函数或属性
// 缺点:跟借用构造函数一样,每次创建对象都会创建一遍方法
var ob = {
name: "xiaopao",
friends: ["lulu", "huahua"],
};
function CreateObj(o) {
function F() {} // 创建一个构造函数F
F.prototype = o;
return new F();
}
// 上面CreateObj函数 在ECMAScript5 有了一新的规范写法,Object.create(ob) 效果是一样的 , 看下面代码
var ob1 = CreateObj(ob);
console.log(ob1.name); // xiaopao
7.6、寄生组合式继承
在混合继承的基础上,把原型链的继承该成在父类的原型的基础上创建 子类.prototype = object.create(父类.peototype)
// 优点:完美继承
// 缺点:代码繁多,使用起来十分麻烦
function Parent(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
Parent.prototype.sayName = function () {
console.log(this.name);
};
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
function CreateObj(o) {
function F() {}
F.prototype = o;
return new F();
}
// Child.prototype = new Parent(); // 这里换成下面
function prototype(child, parent) {
var prototype = CreateObj(parent.prototype);
prototype.constructor = child;
child.prototype = prototype;
}
prototype(Child, Parent);
var child1 = new Child("xiaopao", 18);
console.log(child1);
7.7、es6 继承
class Child extends Parent {}
8、递归和递归优化
递归就是函数自己调用自己。但是又不能无限的调用自己,需要有一个出口,否则会成为死循环。函数调用是通过栈(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
9、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("&");
}
10、跨域
跨域:协议域名端口号只要有一个不一样就会产生跨域问题 解决方法: 1.cors 就是服务端在HTTP返回头上加上“AccessControll-Allow-Origin:*” 2.通过proxy代理 3.jsonp 利用script的src属性没有跨域限制,将要访问的地址 赋值给src ,服务器返回一个函数,调用函数,传递一个jsonp格式的参数 4.cors中间件:哪个响应头被拦截 就头设置cors响应头,解除浏览器的限制
跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的。其 实我们通常所说的跨域是狭义的,是由浏览器同源策略限制的一类请求场景。同源策略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'
11、事件流和事件委托
事件流一般分三个阶段:1、捕获阶段(由外向内) 2、目标阶段 (执行阶段) 3、冒泡阶段(由内向外)
阻止事件冒泡e.stopPropagation() 阻止默认动作e.preventDefault()
事件委托:就是把事件委托给父级,利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
事件委托:将事件加给父元素,事件发生时,通过事件目标e.target找到对应的子元素去做 事件流一般分三个阶段:1、捕获阶段(由外向内) 2、目标阶段 (执行阶段) 3、冒泡阶段(由内向外) 阻止事件冒泡e.stopPropagation() 阻止默认动作e.preventDefault()
12、事件循环
同步任务进入主线程,异步任务进入Event Table并注册函数 当指定的事情完成时,Event Table会将这个函数移入Event Queue。 主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执 行。 上述过程会不断重复,也就是常说的Event Loop(事件循环)。
同步任务进入主线程执行,异步任务放在任务队列中等待执行,等主线程的同步任务执行完毕之后,再去执行任务队列中的异步任务,此上操作重复执行, 被称为事件循环
console.log("script start");
setTimeout(function () {
console.log("setTimeout");
}, 0);
Promise.resolve()
.then(function () {
console.log("promise1");
})
.then(function () {
console.log("promise2");
});
console.log("script end");
/* 执行结果为:script start, script end, promise1, promise2, setTimeout因为Promise是微任务,主线程会在同步任务做完后先清空微任务队列,再执行宏任务队列 */
微任务是由JavaScript自身发起,包括:process.nextTick、promise、MutationObserver
宏任务是由宿主发起的,如浏览器、node。包括:setTimeout、setInterval、setImmediate、postMessage
事件循环又叫消息循环,是浏览器渲染主线程的工作方式。 在谷歌浏览器的源码中,他开启一个不会结束的for循环,每次循环从消息队列中取出第一个任务执行,而其他线程只需要在合适的时候将任务加入到队列末尾即可。 过去把消息队列简单分为宏队列和微队列,这种说法目前已无法满足复杂的浏览器环境,取而代之的是一种更加多变的处理方式。 根据w3c官方的解释,每个任务有不同的类型,同类型的任务必须在同一个队列,不同的任务可以属于不同的队列。不同任务对列有不同的优先级,在一次事件循环中,由浏览器自行决定取哪一个队列的任务。但浏览器必须有一个微队列,微队列的任务一定具有最高的优先级,必须优先调用执行。 露件循环又叫做消息循环,是浏览器渲染主线程的工作方式。 在 Chrome 的源码中,它开启一个不会结束的 for 循,每次循环从消息队列中取出第一个任务执行,而其他线程只需要在合适的时候将任务加入到队列未尾即可。 过去把消息队列简单分为宏队列和微队列,这种说法目前已无法满足复杂的浏览器环境,取而代之的是一种更加灵活多变的处理方式。 根据 W3C官方的解释,每个任务有不同的类型,同类型的任务必须在同一个队列,不同的任务可以属于不同的队列。不同任务队列有不同的优先级,在一次事件循环中,由浏览器自行决定取哪一个队列的任务。但浏览器必须有一个微队列,微队列的任务一定具有最高的优先级,必须优先调度执行。 先执行微任务 在执行宏任务
13、防抖和节流
防抖:事件发生时开启一个定时器,如果定时器没有执行完就有触发该事件,定时器会重新计算时间 节流:事件发生时开启一个定时器,如果定时器没有执行完就有触发该事件,此事件会被禁止,当定时器执行完时,会解封事件
// 节流:在计时器内部清除计时器,有节奏的执行事件
function throttle(callback, delay = 1000){
let timer = null;
function f(){
if(!timer){
timer = setTimeout(() => {
callback && callback.call(this);
clearTimeout(timer);
timer = null;
}, delay);
}
}
return f;
}
// 防抖:在计时器前边清除计时器,只执行最后一次事件,能够无限延长执行时间
function debounce(callback, delay = 1000) {
let timer = null;
function f() {
clearTimeout(timer);
timer = setTimeout(() => {
callback && callback.call(this);
}, delay);
}
return f;
}
防抖的原理 在事件处理函数中添加一个定时器 在定时器中执行代码 延迟一定的时间执行代码 如果你在定时器执行过程中再次触发事件 把原先的定时器清除,再重新定义
function debounce(fun, wait) {
var timer;
function show() {
// 如果timer有值 定时器没有走完
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(fun, wait)
}
return show
}
节流的原理 在事件处理函数中添加一个延时定时器,延迟一定的时间执行代码 在当前定时器没有完成之前 禁止事件执行 等定时器完成之后再允许事件执行
function throttle(fun, wait) {
var timer;
function show() {
// 如果timer有值 定时器没有结束 没有值的时候才去执行
if (!timer) {
timer = setTimeout(function () {
fun();//要做的事有很多 放入函数执行
timer = undefined;
}, wait)
}
}
return show
}
14、深克隆和浅克隆
深拷贝:如果对象是复杂数据类型,拷贝的则是内容 不会一改全改 浅拷贝:如果对下行那个是复杂数据类型,拷贝的则是同一内存地址 会一改全改
浅克隆:同值也同址。浅拷贝是指源对象与拷贝对象共用一份实体,仅仅是引用的变量不同(名称不同)。对其中任何一个对象的改动都会影响另外一个对象。如:Object.assign;=等号赋值;slice截取。
深克隆:同值不同址。深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。如:JSON.parse(JSON.stringify());
function deepClone(target) {
// 定义一个变量
let result;
// 如果当前需要深拷贝的是一个对象的话
if (typeof target === "object") {
// 如果是一个数组的话
if (Array.isArray(target)) {
result = []; // 将result赋值为一个数组,并且执行遍历
for (let i in target) {
// 递归克隆数组中的每一项
result.push(deepClone(target[i]));
}
// 判断如果当前的值是null的话;直接赋值为null
} else if (target === null) {
result = null;
// 判断如果当前的值是一个RegExp对象的话,直接赋值
} else if (target.constructor === RegExp) {
result = target;
} else {
// 否则是普通对象,直接for in循环,递归赋值对象的所有值
result = {};
for (let i in target) {
result[i] = deepClone(target[i]);
}
}
// 如果不是对象的话,就是基本数据类型,那么直接赋值
} else {
result = target;
}
// 返回最终结果
return result;
}
function deepCopy(data) {
// 创建一个空间,判断是创建空数组还是空对象,判断data参数的数据类型
if (getType(data) === "[object Array]") {
var res = [];
} else if (getType(data) === "[object Object]") {
var res = {};
} else {
return data;
}
// 判断data参数中的元素是不是复杂数据类型,如果是复杂数据类型将之前的逻辑再走一次
for (var i in data) {
if (getType(data[i]) === "[object Array]" || getType(data[i]) === " [object Object]") {
res[i] = deepCopy(data[i]);
} else {
res[i] = data[i];
}
}
return res;
}
// 检测数据类型封装
function getType(data) {
return Object.prototype.toString.call(data);
}
15、cookie、sessionStorage和localStorage的区别
localstorage 本地存储.长期存储在浏览器 需要手动销毁 除非被清除,否则永久保存 一般为5MB sessionstorage 会话存储 关闭当前会话就会销毁 cookie 存储在客户端 过期时间默认浏览器关闭过期 大小4kb 个数50个 数据格式只能是字符串 不同浏览器不共享cookie 相同项目不同页面之间可共享cookie Cookie cookie 它的大小限制为4KB左右。它的主要用途有保存登录信息, 一般由服务器生成,可设置失效时间。如果在浏览器端生成Cookie,默认是关闭浏览器后失效 每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能问题 大小4kb
15.1、cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递,而sessionStorage和localStorage不会自动把数据发送给服务器,仅在本地保存。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下 15.2、存储大小限制也不同,cookie数据不能超过4K,同时因为每次http请求都会携带cookie、所以cookie只适合保存很小的数据,如会话标识。sessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大 15.3、数据有效期不同,sessionStorage:仅在当前浏览器窗口关闭之前有效;localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;cookie:只在设置的cookie过期时间之前有效,即使窗口关闭或浏览器关闭 15.4、作用域不同,sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面;localstorage在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的 15.5、web Storage支持事件通知机制,可以将数据更新的通知发送给监听者 15.6、web Storage的api接口使用更方便
16、get和post请求的区别
GET在浏览器回退时是无害的,而POST会再次提交请求。
GET产生的URL地址可以被Bookmark,而POST不可以。
GET请求会被浏览器主动cache,而POST不会,除非手动设置。
GET请求只能进行url编码,而POST支持多种编码方式。
GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
GET请求在URL中传送的参数是有长度限制的,而POST么有。
对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
GET参数通过URL传递,POST放在Request body中
17、new操作符都做了哪些事情
构造函数中没有显示的创建Object对象,实际上后台自动创建了一个空对象,直接给this对象赋值属性和方法,this即指向创建的对象。没有return返回值,后台自动返回了该对象,该对象继承构造函数的原型
// 模拟构造函数实现 var Book = function(name) { this.name = name; }; //正常用法 var js = new Book('js'); //使用代码模拟,在非IE浏览器中测试,IE浏览器不支持 var javascript = {}; javascript.__proto__ = Book.prototype; Book.call(javascript, 'js');
1、隐式创建了一个空对象,让this指向这个空对象 2、执行构造函数中的代码 3、让实例的__proto__指向构造函数的prototype 4、隐式的返回这个对象
18、XSS攻击和CSRF攻击
XSS:跨站脚本攻击Cross site script,因叫css容易让人误会所以改成了xss。比如一个JSON数据:
var obj = [ { id: 1, name: "<script>alert('哈哈哈')</script>", age: 12, } ];
在不该出现script代码的地方出现了,引发一些潜在的危险。 XSS漏洞,能让人们在网页里面插入一段有功能的语句。 XSS 全称“跨站脚本”,是注入攻击的一种。其特点是不对服务器端造成任何伤害, 而是通过一些正常的站内交互途径,例如发布评论,提交含有 JavaScript 的内容文本。这时服务器端如果没有过滤或转义掉这些脚本,作为内容发布到了页面上,其他用户访问这个 页面的时候就会运行这些脚本。 防范: ① 用正则表达式阻止用户提交带有<、eval、script等危险字眼的语句 ② 显示的时候不要直接用innerHTML,而是用innerText,或者将<转义。
CSRF 的全称是“跨站请求伪造”,而 XSS 的全称是“跨站脚本”。看起来有点相 似,它们都是属于跨站攻击——不攻击服务器端而攻击正常访问网站的用户,但前面说 了,它们的攻击类型是不同维度上的分类。CSRF 顾名思义,是伪造请求,冒充用户在站内 的正常操作。我们知道,绝大多数网站是通过 cookie 等方式辨识用户身份(包括使用服务 器端 Session 的网站,因为 Session ID 也是大多保存在 cookie 里面的),再予以授权的。 所以要伪造用户的正常操作,最好的方法是通过 XSS 或链接欺骗等途径,让用户在本机(即 拥有身份 cookie 的浏览器端)发起用户所不知道的请求。 就是说,如果用户不老老实实写姓名,写了一个个<script>叫做XSS。如果进一步的,写了一个$.post()发了document.cookie就是CSRF了。解决方法: ① 用token验证,验证用户的IP地址生成MD5码,更安全的验证方法 ② 防住XSS。
什么是XSS Cross-Site Scripting(跨站脚本攻击),简称XSS,是一种代码注入攻击。攻击者通过在目标网站上注入恶意脚本,使之在用户的浏览器上运行。利用这些恶意脚本,攻击者可获取用户的敏感信息如Cookie、SessionID等,进而危害数据安全 什么是CSRF Cross-site request forgery(跨站请求伪造),是一种挟持用户在当前已登陆的Web应用程序上执行非本意的操作的攻击方法。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。
19、垃圾回收机制
一般来说没有被引用的对象就是垃圾,就是要被清除, 有个例外如果几个对象引用形成一个环,互相引用,但根本访问不到它们,这几个对象也是垃圾,也要被清除。 垃圾回收的方法主要有两种:一种是标记清除,即用完之后的变量 赋值成null,另一种 是引用计数,将使用完的对象的引用计数,如果为0则回收
js的垃圾回收机制: 是js中定义回收资源的一种机制,每隔一段时间 js的执行环境就会去清理没用的数据 垃圾回收机制有两种方法:标记清除回收 引用计数回收 引用计数回收:每次使用变量 计数+1 当计数是0 就会被回收掉
20、常用DOM操作
createElement 创建
appendChild末尾添加
insertBefore 前边插入
cloneNode(true) 克隆
removeChild() 移除
parentNode父节点
childNodes // 全部子节点
firstChild // 第一个子节点
lastChild // 最后一个子节点
previousSibling // 上一个兄弟节点
nextSibling // 下一个兄弟节点
获取dom节点:document.getElementById() 、document.getElementsByTagName() 、document.getElementsByClassName() 、document.getElementsByName() 、document.querySelector() 、document.querySelectorAll()
21、AMD、CMD、ES6、CommonJS的区别
CommonJS:模块引用(require) 模块输出(exports) 模块标识(module) ES6:模块引用(import) 模块输出(export) 前者支持动态导入,也就是 require(${path}/xx.js),后者目前不支持。 前者是同步导入,因为用于服务端,文件都在本地,同步导入即使卡住主线 程影响也不大。而后者是异步导入,因为用于浏览器,需要下载文件,如果也采用同 步导入会对渲染有很大影响。 前者在导出时都是值拷贝,就算导出的值变了,导入的值也不会改变,所以 如果想更新值,必须重新导入一次。但是后者采用实时绑定的方式,导入导出的值都 指向同一个内存地址,所以导入值会跟随导出值变化 AMD、CMD都使用define定义模块,require引入模块,区别在于AMD是前置依赖, CMD是就近依赖
// AMD 依赖必须一开始就声明 define(["./a", "./b"], function (require, factory) { // do something... }); // CMD define(function(require, factory) { var a = require('./a'); // 依赖就近书写 // do something... });
更多推荐
前端面试题(js)
发布评论