很长一段时间没看js书籍了,最近抽点时间,回顾一些js基础的知识点,写了半天,朋友问个问题,解决一下bug,再回来写,都没了,又从新写。
(一)javaScript 浅拷贝与深拷贝
1:简单的数据类型
它们的值占据了内存中固定大小的空间和位置,并被保存在栈内存中。当一个变量向另一个变量赋值基本类型的值,会创建这个值的一个副本,还有就是不能给基本数据类型的值添加属性
var a = 1;
var b = a;
a.attr = 'ciyt';
console.log(a.attr)//undefined
VM1600:4 undefined复制代码
上面代码中a就是简单数据类型(Number),b就是a的副本,它们两者都占有不同位置但相等的内存空间。
2:复杂数据类型
复杂数据类型的引用赋值,一般指向的是统一个对象,因为他们的引用指针是一样的,赋值的其实是内存里面的地址指针。
var obj = {
name:'liu',
age:'28'
}
var obj2 = obj;
obj2.sex="boy";
console.log(obj);
console.log(obj2);
VM1733:7 {name: "liu", age: "28", sex: "boy"}
VM1733:8 {name: "liu", age: "28", sex: "boy"}
复制代码
我们可以看到obj赋值给obj2后,当我们给一个obj2对象的添加属性值,两个对象都发生了改变,究其原因局势因为obj和obj2这两个变量都指向同一个指针,赋值只是复制了指针,所以当我们改变其中一个的值就会影响另外一个变量的值。
这段代码就是我们常说的 浅拷贝,改变其中一个对象,另外一个对象也会改变。
下面我们来讲解一下深拷贝的方法,分数组【Array】和对象【Object】
数组
对于数组我们可以使用slice()
和concat()
方法来解决上面的问题
var arry = [1,2,'a'];
var arry2 = arry;
arry2.push('b');
console.log('arry',arry)
console.log('arry2',arry2)
VM1974:4 arry (4) [1, 2, "a", "b"]
VM1974:5 arry2 (4) [1, 2, "a", "b"]
复制代码
slice()方法:
var arry = [1,2,'a'];
var arry2 = arry.slice(0);
arry2.push('b');
console.log('arry',arry)
console.log('arry2',arry2)
VM2057:4 arry (3) [1, 2, "a"]
VM2057:5 arry2 (4) [1, 2, "a", "b"]
复制代码
concat()方法:
var arry = [1,2,'a'];
var arry2 = arry.concat();
arry2.push('b');
console.log('arry',arry)
console.log('arry2',arry2)
VM2076:4 arry (3) [1, 2, "a"]
VM2076:5 arry2 (4) [1, 2, "a", "b"]
复制代码
对象
我们可以创建一个新对象 new Object() 或者封装一个深度拷贝的函数
var obj = {
name:'liu',
age:'28'
}
var obj2 = obj;
obj2.sex="boy";
console.log('obj',obj);
console.log('obj2',obj2);
VM446:7 obj {name: "liu", age: "28", sex: "boy"}
VM446:8 obj2 {name: "liu", age: "28", sex: "boy"}
复制代码
new Object()
var obj = {
name:'liu',
age:'28'
}
var obj2 = new Object();
obj2.name="liu";
obj2.age="28";
obj2.sex="boy";
console.log('obj',obj);
console.log('obj2',obj2);
VM508:9 obj {name: "liu", age: "28"}
VM508:10 obj2 {name: "liu", age: "28", sex: "boy"}
复制代码
封装一个deepCopy函数
var obj = [1,2,'a'];
function deepCopy(obj){
var newObj = obj instanceof Array ? [] : {};
for(var item in obj){
var temporary = typeof obj[item] == 'Object' ? deepCopy(obj[item]) : obj[item];
newObj[item] = temporary;
}
return newObj
}
var obj2 = deepCopy(obj);
obj2.push('b');
console.log('obj',obj);
console.log('obj2',obj2);
VM1154:12 obj (3) [1, 2, "a"]
VM1154:13 obj2 (4) [1, 2, "a", "b"]
复制代码
var obj = {
name:'liu',
age:'28'
};
function deepCopy(obj){
var newObj = obj instanceof Array ? [] : {};
for(var item in obj){
var temporary = typeof obj[item] == 'Object' ? deepCopy(obj[item]) : obj[item];
newObj[item] = temporary;
}
return newObj
}
var obj2 = deepCopy(obj);
obj2.sex = 'boy';
console.log('obj',obj);
console.log('obj2',obj2);
VM1222:15 obj {name: "liu", age: "28"}
VM1222:16 obj2 {name: "liu", age: "28", sex: "boy"}
复制代码
(二)jsonp原理
首先JSON是一种基于文本的数据交换方式,或者叫做数据描述格式,另外一种常见的是XML格式,当一个网页在请求JavaScript文件时则不受是否跨域的影响,凡是拥有”src”这个属性的标签都拥有跨域的能力,比如<script>、<img>、<iframe>
所以我们这里运用了script标签的跨域能力,让它用一个callback函数包裹着一段JSON格式的数据,当该数据返回到前端页面的时候,我们再执行这个函数就可以把数据读取出来
前端代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>What's jsonp</title>
</head>
<body>
<button onclick="jsonpServer('jsonp.php')">JSONP</button>
</body>
<script>
function jsonpServer(url) {
var script = document.createElement("script");
script.setAttribute("type", "text/javascript");
script.setAttribute("src", url);
document.body.appendChild(script);
}
function JSON_CALLBACK(data) {
console.log(data);
}
</script>
</html>复制代码
后端代码
<?php
$data = '[{"id":"1","name":"wsscat"},{"id":"2","name":"asw"}]';
$data = "JSON_CALLBACK(" . $data . ")";
echo $data;
?>复制代码
jQuery 的Ajax写法
$.ajax({
url:'index.php',
type:'get',
dataType:'jsonp',
//jsonp:'JSON_CALLBACK',
jsonpCallback:'JSON_CALLBACK',
success:function(data){
console.log(data)
}
})
复制代码
后端代码
<?php
$data = '[{"id":"1","name":"wsscat"},{"id":"2","name":"asw"}]';
$data = "JSON_CALLBACK(" . $data . ")";
echo $data;
?>复制代码
(三)JavaScript 中的 call 与 apply 继承方法
call的意思就是把Animal构造的方法应用到cat这个对象身上,也就是Animal的属性创建到了cat里面,所以cat就继承了Animal的方法
function Animal(a, b) {
this.type = 'Animal'
this.behavior = function(){
console.log(this.type+" is running")
}
}
function cat(a, b) {
this.name = 'goodCat'
//这里call的意思就是把Animal的方法应用到cat这个对象身上
//所以cat就继承了Animal的方法
Animal.call(this);
}
console.log(new cat())VM2223:15 cat {name: "goodCat", type: "Animal", behavior: ƒ}behavior: ƒ ()name: "goodCat"type: "Animal"__proto__: Object复制代码
call实现多重继承 (ps:我们可以继承多个构造函数,这就是多重继承)
function Animal(a, b) {
this.type = 'Animal'
this.behavior = function(){
console.log(this.type+" is running")
}
}
function ageCat(a, b) {
this.age = 0
}
function cat(a, b) {
this.name = 'goodCat'
//这里call的意思就是把Animal的方法应用到cat这个对象身上
//所以cat就继承了Animal的方法
Animal.call(this);
//这里call的意思就是把ageCat的方法应用到cat这个对象身上
//所以cat就继承了ageCat的方法
ageCat.call(this);
}
console.log(new cat())VM2350:22 cat {name: "goodCat", type: "Animal", behavior: ƒ, age: 0}age: 0behavior: ƒ ()name: "goodCat"type: "Animal"__proto__: Object复制代码
apply与call的区别
其实apply和call这两个方法基本上是差不多的,区别在于call的第二个参数可以是任意类型,而apply的第二个参数必须是数组,也可以是arguments(即传给构造函数的参数)。
继续优化继承
如果构造函数this绑定太多属性(比如一些共同方法),在实例化后会造成浪费,为此我们一般会使用原型链来优化,但是使用原型链之后我们的apply和call的继承方法就会失效
为此我们一般使用混合的写法,使用原型链和(apply或者call)方法进行继承
具体两句话
让子的原型链指向父的实例(父实例化的对象)cat.prototype = new Animal();
让父的属性创建在子的this上Animal.call(this, type)
整体代码如下,那么就会让父原型链的属性和this上的属性都得到继承
function Animal(type) {
this.type = type
this.behavior = function() {
console.log(this.type + " is running")
}
}
Animal.prototype.action = function() {
console.log("running")
}
function cat(name, type) {
this.name = name
Animal.call(this, type)
}
cat.prototype = new Animal();
console.log(new cat('xiaohong', 'beautiful'));
(new cat('xiaohong')).action() //runningVM2490:17 cat {name: "xiaohong", type: "Animal", behavior: ƒ}behavior: ƒ ()name: "xiaohong"type: "Animal"__proto__: Animal复制代码
(四)sessionstorage,localstorage和cookie
cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递。而sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下。存储大小限制也不同,cookie数据不能超过4k,同时因为每次http请求都会携带cookie,所以cookie只适合保存很小的数据,如会话标识。
sessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大。数据有效期不同,
sessionStorage:仅在当前浏览器窗口关闭前有效,自然也就不可能持久保持;
localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;
cookie只在设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭。作用域不同,sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面;
localStorage在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的。Web Storage 支持事件通知机制,可以将数据更新的通知发送给监听者。Web Storage 的 api 接口使用更方便。
总结:
- localStorage没有过期时间,只要不clear或remove,数据会一直保存。
- sessionStorage 针对一个session进行数据存储,生命周期与session相同,当用户关闭浏览器后,数据将被删除。