浏览器缓存,有何区别?
- localStorage和sessionStorage一样都是用来存储客户端临时信息的对象。他们均只能存储字符串类型的对象
- localStorage生命周期是永久,除非自己清除
- sessionStorage生命周期为当前窗口或标签页,一旦窗口或标签页被永久关闭了,那么所有通过sessionStorage存储的数据也就被清空了。
- LocalStorage 一般不会自动过期(除非用户手动清除),而 SessionStorage 在会话结束时过期(如关闭浏览器)
常用的数组API?
MDN中简绍数组API的详细链接
push、pop、unshift、shift、splice、slice、map、reduce、concat、filter、some、forEach、find、findIndex等
**push()**
方法将一个或多个元素添加到数组的末尾,并返回该数组的新长度。unshift()
方法将一个或多个元素添加到数组的开头,并返回该数组的新长度(该方法修改原有数组)。**pop()**
方法从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度。**shift()**
方法从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度。splice()
方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。**slice()**
方法返回一个新的数组对象,这一对象是一个由begin
和end
决定的原数组的浅拷贝(包括begin
,不包括end
)。原始数组不会被改变。**map()**
方法创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。**concat()**
方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。**filter()**
方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素,不改变原有数组。**some()**
方法测试数组中是不是至少有1个元素通过了被提供的函数测试。它返回的是一个Boolean类型的值。**forEach()**
方法对数组的每个元素执行一次给定的函数。返回值是undefined
。数组去重
方式一:用ES6的set
用set不接受重复元素的特性
缺点:无法去掉空对象{}和[] ```javascript function unique (arr) { return Array.from(new Set(arr)) } var arr = [1,1,’true’,’true’,true,null,null, NaN, NaN,’NaN’, 0, 0, ‘a’, ‘a’,{},{}]; console.log(unique(arr)) //[1, “true”, true, null, NaN, “NaN”, 0, “a”, {…}, {…}]
//或者使用set与扩展运算符配合 […new Set([1, 1, 2, 3, 4, 4])];//[1, 2, 3, 4]
<a name="item-3"></a>
## 方式二:利用for嵌套for,然后splice去重(ES5中最常用)**
`splice(从第几个开始,删除几个)`,这只是splice的一种用法<br />**缺点:**NaN和{}没有去重,两个null直接消失了
```javascript
function unique(arr){
for(var i=0; i<arr.length; i++){
for(var j=i+1; j<arr.length; j++){
if(arr[i]==arr[j]){ //第一个等同于第二个,splice方法删除第二个
arr.splice(j,1);
j--;
}
}
}
return arr;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "true", 15, false, undefined, NaN, NaN, "NaN", "a", {…}, {…}] //NaN和{}没有去重,两个null直接消失了
方法三:利用indexOf
数组.indexOf(x);
如果数组里找到了x就返回第一个索引值,如果没有就返回-1
注意:if()
判断里,只要不是false就都是真;-1,1,0也是真
function unique(arr) {
var array = [];
for (var i = 0; i < arr.length; i++) {
if (array .indexOf(arr[i]) === -1) {
array .push(arr[i])
}
}
return array;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
// [1, "true", true, 15, false, undefined, null, NaN, NaN, "NaN", 0, "a", {…}, {…}] //NaN、{}没有去重
方法四:sort()
sort()
是根据ascoll码值进行排序,所以说相同的两个数据会在一起
如果不将arrry= [arr[0]]
;最后返回的数组会缺少第一个数据
function unique(arr) {
arr = arr.sort()
var arrry= [arr[0]];
for (var i = 1; i < arr.length; i++) {
//只有相邻的两个不相同,才会把后面的那个加进去,这样永远都是拿新的数据和已经在array里的最后一个相比较
if (arr[i] !== arr[i-1]) {
arrry.push(arr[i]);
}
}
return arrry;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
// [0, 1, 15, "NaN", NaN, NaN, {…}, {…}, "a", false, null, true, "true", undefined] //NaN、{}没有去重
对象API
keys方法,用于遍历对象可枚举属性;create用于创建指定原型的新对象;getPrototypeOf用于获取对象原型;definedProperty用于自定义对象是否可枚举,是否可写等属性。assign用于复制多个源对象属性到目标对象等;
对象的API链接-MDN
**Object.defineProperty()**
方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。通过**Object.defineProperty()**
添加的属性值不会被修改 ```javascript const obj1 = {};
//给obj1对象添加一个propty1的属性,并且值为42 Object.defineProperty(obj1, ‘property1’, { value: 42, writable: false }); //虽然此时修改了property1的值,但是并没有改变 obj1.property1 = 77;
console.log(obj1.property1); // 打印出来的还是42
- **`Object.create()`**方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
- `**Object.keys()**` 方法会**返回一个由一个给定对象**的**自身可枚举属性组成的数组(就是返回对象的下表组成的数组)**,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致 。
<br />
<a name="laVqY"></a>
# 作用域和作用域链
作用域是变量和函数的作用范围与生命周期,当在当前作用域查找某变量时,如果没找到就会去上层作用域找,此行为可以一直找到全局对象window(非严格模式),而这个查找过程也就是所谓的作用域链。<br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/1811610/1607864342026-2a73fab8-c476-4314-9cd0-b218d4db32c3.png#align=left&display=inline&height=146&margin=%5Bobject%20Object%5D&name=image.png&originHeight=291&originWidth=599&size=31588&status=done&style=none&width=299.5)
<a name="idqpK"></a>
# 原型与原型链
[https://www.php.cn/js-tutorial-417938.html](https://www.php.cn/js-tutorial-417938.html)
<a name="1KQHM"></a>
## Prototype原型
> 我们创建的每个函数都有一个 prototype(原型) 属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。
简单来说,就是当我们创建一个函数的时候,系统就会自动分配一个 prototype属性,可以用来存储可以让所有实例共享的属性和方法<br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/1811610/1607864841441-73a1164a-48ac-4263-a177-b60543575888.png#align=left&display=inline&height=252&margin=%5Bobject%20Object%5D&name=image.png&originHeight=363&originWidth=661&size=45404&status=done&style=none&width=458)
- 每一个构造函数都拥有一个 prototype 属性,这个属性指向一个对象,也就是原型对象
- 原型对象默认拥有一个 constructor 属性,指向指向它的那个构造函数
- 每个对象都拥有一个隐藏的属性 __proto__,指向它的原型对象
```javascript
function Person(){}
var p = new Person();
p.__proto__ === Person.prototype // true
Person.prototype.constructor === Person // true
原型链
JavaScript 中所有的对象都是由它的原型对象继承而来。而原型对象自身也是一个对象,它也有自己的原型对象,这样层层上溯,就形成了一个类似链表的结构,这就是原型链
- 所有原型链的终点都是
Object
函数的prototype
属性 - Objec.prototype 指向的原型对象同样拥有原型,不过它的原型是 null ,而 null 则没有原型
总结
- 对象. proto === 构造函数.prototype
- 一个对象的原型指的是这个对象与其他同类对象的公有属性的集合
- 一个对象的原型的地址存在这个对象的proto属性里
- 每个对象都有原型,但除了「根对象
Object.prototype
比较特殊,Object.prototype
这个对象的原型为null
闭包
闭包:如果一个函数用到了外部的变量,那么这个函数加这个变量,就叫做闭包
闭包的作用:闭包常常用来‘间接访问一个变量’。换句话说,隐藏一个变量。就是当你需要延长变量的生命周期的时候
优点:
- 可以避免使用全局变量,防止全局变量污染
缺点:
- 会造成内存泄漏(有一块内存空间被长期占用,而不被释放)
ES6
说说你对箭头函数的理解
箭头函数一般用于回调,需要注意的就是箭头函数没有自己的this,它的this更像是一个变量,由外层作用域的this来决定,别人指向谁它就指向谁。我们不能直接修改箭头函数this指向,但可以通过修改外层this达到间接修改this的目的。也正是因为这个特性,箭头函数不能用于做构造函数
实现数组随机排序
这属于洗牌算法
随机产生一个树,这个随机数要小于数组的长度,然后向下取整
遍历这个数组,每一次与这个随机数为下标的值进行两两交换
function shuffle(A) {
for (var i = A.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var t = A[i]; A[i] = A[j]; A[j] = t;
}
};
let arr = [1,2,3,4,5,6,7,8,9,10];
shuffle(arr);
刁钻考题
[1,2,3].map(parseInt)输出什么?
[1,2,3].c(parseInt)
//输出[1, NaN, NaN]
这是因为:parseInt 经常被带着一个参数使用, 但是这里接受两个。第一个参数是一个表达式而第二个是callback function的基,
写出输出结果
第一题
var a = b = c = {};
a.val = 1;
b.val = 2;
console.log(a.val);
console.log(b.val);
console.log(c.val);
//结果是3个2
//代码等同于
c = {};
b = c;
var a = b;
a.val = 1;
b.val = 2;
由于对象属于引用类型数据,所以b与c都是保存的对象c的地址引用,不管谁改,都会影响的同一个对象,最后一次操作将val改为了2,所以三个变量访问都是输出2。
第二题
var a = 1;
(function () {
console.log(a);
var a = 2;
a++;
})();
//输出undefined
//代码等同于
var a = 1;
(function () {
var a;
console.log(a);
a = 2;
a++;
})();
变量声明提升到当前作用域的顶部,所以此时a并没有复制,于是输出undefined
。
js的大bug
[] == ![] // true
NaN表示理解不了的数字
年月日,时分秒;只有月份从0开始
不是按照数字排序的,先排1开头的,再排2开头的(所以不要使用默认的sort排序)
把伪数组转化为数组
es6已经补全这个bug了;使用Array.from()
es6之前的语法使用立即执行函数
!function(){
var a=1
}()
es6添加了块级作用域
{
let b=1
}