学习笔记

学习笔记基本类型与引用类型
在这里想说一下基本类型与引用类型的两大点不同,1、可以进行的操作不同。2、复制的方式不同。
我们可以为一个引用类型值例如对象,添加属性和方法,但是对于基本类型值就不能这么操作了。

var obj = new Objec();
obj.name = 'hyt';
console.log(obj.name);//hyt;

var str  = 'hyt';
str.age = 18;
console.log(str.age);//undefined;
//只能给引用类型值动态地添加属性及方法;

另外关于复制的方式也是不一样的。基本类型值保存在栈中,引用类型值保存在堆中。复制引用类型值时,它的副本是一个指针,指向堆中的一个对象,复制操作完后,两个变量将指向堆中的同一个对象。因此,改变一个变量,将影响另一个变量。
例如:

//引用类型
var obj = new Object();
obj1 = obj;
obj1.name = 'hyt';
var obj2 = obj1;
obj2.name = 'wl';
console.log(obj1.name); // wl;
//基本类型
var str = 5;
var str1 = str;
str1 = 10;
console.log(str);//5 

以上就是基本类型值与引用类型值的不同,那么怎样复制引用类型值,可以不改变它之前的值呢?这里简单说一下深拷贝及浅拷贝。

浅谈深拷贝及浅拷贝

所谓浅拷贝就是复制一下引用类型值,旧变量会随新变量一起改变。而当我们的目标是改变引用类型值,旧变量不随新变量改变,那就需要进行深拷贝。
关于深拷贝还细分只拷贝第一层级,我们深拷贝第一层级一个数组和对象来做个栗子。
数组的深拷贝第一层级方法总结:

  1. slice() :从已有的数组中截取一部分元素片段组成的新数组(不改变原来的数组!)
    用法:array.slice(start,end);start表示是起始元素的下标,end表示的是终止元素的下标。当slice()不带任何参数的时候,默认返回一个长度和原数组相同的新数组
  2. concat():用于连接两个或多个数组。( 该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。)
    用法:array.concat(array1,array2,......,arrayN)因为我们上面调用concat的时候没有带上参数,所以var copyArray = array.concat();实际上相当于var copyArray = array.concat([]);也即把返回数组和一个空数组合并后返回
  3. 直接遍历;循环遍历数组中的值,把它复制给新的数组;
	var arr = [1,2,3,4,5];
	function copy(array) {
		var newArr = [];
		for (let item of array){
			newArr.push(item);
	    };
	    return newArr;
	}
 	var newArrVal = copy(arr);
	newArrVal[0] = 'hyt';
    console.log(arr);
    console.log(newArr);//错误,你要打印的是函数返回来的值,应该把函数返回的值赋给一个变量,而不是直接打印newArr;newArr依然是一个空数组。
    console.log(newArrVal);

MY PROMBLEM:
function c() {
	var newArr = [1,2,3];
}
console.log(newArr);//为啥打印出来是[];不是undefined?因为newArr是局部变量所以赋值不会在函数外部得到,但是被声明了所以不会是undefined。


MY PROMBLEM:
使用箭头函数完成上述功能;

MY PROMBLEM:
es6的generator的用处,可以做一些面向对象做的事情,可以更方便的写一个斐波那契数,可以将多层嵌套的ajax变得更为优雅,可以解决闭包也能解决的循环返回最后值的Bug,它就是可以返回多个函数的生成器。

深拷贝一级对象
对象的深拷贝第一层级方法总结:

  1. es6中的Object.assign。
  2. 循环遍历对象拷贝。
	var obj = { name: 'hyt' , age: 18};
	function copy(copyObj) {
		let obj1 = {};
		for(let item in copyObj) {
			obj1[item] = copyObj[item];
		};
		return obj1;
	};
	var r = copy(obj);
	r.name = 'wl';
	console.log(obj);
	console.log(r);

以上方法都只适用于数组or对象的一级深拷贝,当数组的元素不再是字符串,或者是对象,数组,当对象是更为复杂的json数据结构,以上方法将无法深拷贝对象。

1、终极大招:递归遍历对象所有属性、属性下的子属性,直至基本类型值进行复制。

var obj1 = {
	 person : {
	 	name : 'hyt',
	 	age : 18
	 }
};
function copy (cObj) {
	let obj;
	//if (typeof cObj !==Object) {
		//return;
	//}
	//cObj intanceof Object ? obj = {} : obj = [];错误写法..
	//也可以写作:
	let obj = cObj.constructor === Object ? {} : [];
	for (let i in cObj) {
		//obj[i] = typeof cObj[i] !== Object ? obj[i] = cObj[i] : copy (cObj[i]) ;
	     if(typeof cObj[i] !== Object) {
	     	obj[i] = cObj[i];
	     } else {
			copy (cObj[i]) ;
		 }
	}
	return obj;
}  
var newO = copy(obj1);
console.log(a);

2、ES6语法扩展运算符...

 let obj = {name:'hyt'};
 let newObj = [...obj];
 //也可以实现深拷贝;

3、JSON.parse() JSON.stringify();

函数中的参数按值传递
在此只说当一个对象作为参数传递到函数中,也是按值传递的,虽然在函数外部可以看到对象的值变了,但是再函数内部再给同名参数new一个新对象,它是不会被改变的。为什么对象是按引用访问的,因为js规定我们不可以直接操作一个对象,只是操作的它的引用。

JS中的高阶函数
即函数作为参数传入到函数中,例如reduce();map();sort()方法的使用,其参数需要一个计算函数。

JS原型链
原型链是一个较为难理解的概念,对比java这样的动态语言来说,它实现继承是通过类的概念,通过接口等方式可以实现继承,而js这样的静态语言没有类的概念(虽然在es6、es7中已经新加了类),它实现继承的方式就是原型链。
为了实现数据共享,减少资源的消耗,所以需要继承的功能。
什么是原型链呢?它又是如何实现继承的呢?首先抛出两个概念,prototype和_proto_。

prototype是函数才有的显示原型链,_proto_是所有对象都有的隐式原型链。(在js中,一切数据类型都是对象,函数也是对象
对象的_proto_指向它的构造函数的prototype,当查找一个对象的属性时,首先查找对象本身有没有该属性,若没有,将向它的构造函数上查找其prototype下是否有该属性,依此规则向上查找,直至找到构造函数的prototype==null时。

var a = function(){};
console.log(a.__proto__ === Function.prototype);// true; a的构造函数是Function;
console.log(a.__proto__ === Object.prototype);// fales; Function的构造函数是Object;不可跨级访问构造函数的protptype;

原型链

把构造函数中不变的属性,挂在prototype上,这样即使构造函数生成了10个实例,实例的prototype上挂的那个属性就是相等的,即它们都指向同一个指针。这样就提高了效率。

例:

function Dog(name) {
	this.name = name;
	this.species = '犬类';//实例中不变的部分可以写在Dog的prototype上,以提高效率。
};
var dogA = new Dog(), dogB = new Dog();
var test1 = dogA.species == dogB.species;//false;A和B是两个不同的实例,属性也指向各自的指针。
Dog.prototype.species = '犬类';
var test2 = dogA.species == dogB.species; //true;
console.log( test1);
console.log( test2 );

学习笔记instanceof

关于检测数据类型的方式之一instanceof
我们知道typeof和instanceof都可以检测一个变量是什么类型的,但是typeof操作符对于基础数据的检测较为方便,不能检测一个对象具体是什么类型。
instansof是用来具体检测引用类型是什么类型的。
语法:
变量 instanceof constructor(构造函数)

var obj = {}; //obj = new Object(); 左侧的写法是右侧的语法糖而已
obj instanceof Object; // true; obj是Object()的实例。
var arr = [];//同1
arr instanceof Array; // true;
var fn = function(){};
fn instanceof Function; // true;
fn instanceof Object();
var date = new Date();
date instanceof Object;//true;

数据类型分引用类型+基本类型,基本类型包含:string、boolean、number、undefind、null,引用类型包含array、object、date等。
所有引用类型值都是Object构造函数的实例,所以检测所有引用类型值和Object构造函数的关系时,都会返回true,检测基本数据都会返回false,因为基本数据都不是对象。

浅谈JS单线程到同步异步

我们都知道javascript是浏览器端的语言,用来操作dom,和用户做一些交互,想象一下如果js是多线程的,那我们同时选择增加一个dom,删除一个dom,那这个时候该执行哪个操作呢?正是因为不想让js过于复杂,所以js是单线程的。(但是为了利用多核cpu的计算能力,h5提出了web woker标准,允许js创建多个线程,但是子线程受主线程控制,且不可操作dom,所以依然不影响js是单线程的本质。)

JavaScript中的线程包括函数调用、I/O设备(如向服务器发送请求获取响应等)、定时器、用户操作的事件(click、keyup、scroll等)。

既然js是单线程的,那就意味着执行线程时需要排队,依次执行任务,但是当我们遇到例如ajax请求数据这样的线程,由于I/O设备很慢,而cpu又是空闲的,这样无形中浪费了cpu的计算能力,于是js意识到,可以把类似I/O设备的线程先放一边,将需要等待的任务暂时挂起,先执行其他主线程的任务,等完事后再执行挂起的任务。

所以就有了同步和异步两种任务。同步任务即为在主线程中排队等待执行的任务,异步任务是被推入队列(task queue)中,等待执行的任务。

异步任务执行规则:

(1)所有同步任务都在主线程上执行,形成一个执行栈
(2)主线程之外,还存在一个”任务队列”。只要异步任务有了运行结果,就在”任务队列”之中放置一个事件。
(3)一旦”执行栈”中的所有同步任务执行完毕,系统就会读取”任务队列”,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。

虽然js是单线程的,但是浏览器是多线程的,轮询event loop的就是浏览器,它有专门做这件事的线程。

EventLoop
计算机系统运行机制
事件环是操作系统的一种运行机制,js中存在3个概念:

  1. 队列
  2. 微队列(micro queue)
堆中主线程任务完成后通知队列中任务执行
队列

认识浏览器中的任务源(task): 微任务:then 、messageChannel 、mutationObersve; 宏任务:setTimeout、setInterval、setTmmediate(只兼容ie)
微队列比队列的优先级高,promise()就是微队列中的操作,而ajax,setTimeout等都是队列中的操作。

例题:

function execution() {
	setTimeout(()=>{
		console.log(1);
	},0);
	new Promise(function e(resolve){
		console.log(2);
		for(let i = 0; i++; i < 9999) {
			i==9999 && resolve();
		}
		console.log(3);
	}).then(function(){
		console.log(4);
	});
	console.log(5);
	//打印顺序:235(4)1
	
}
//eg2
console.log(1);
console.log(2);
setTimeout(function () {
  console.log('setTimeout1');
  Promise.resolve().then(function () {
    console.log('promise');
  });
});
setTimeout(function () {
  console.log('setTimeout2');
});
//1 2 s1 p s2

操作系统知识回顾:一个进程包含多个线程,进程中的内存空间允许线程们共享,但达到上限时需要线程排队…

arguments

简单说一下伪数组,满足伪数组的3点要求:

  1. 是对象。
  2. 必须有length属性,且值是number类型。
  3. 如果这个对象的length不为0,那么必须要有按照下标存储的数据。

常见伪数组:

  1. document.getElementByTagname();返回来的NodeList对象。
  2. function(){};自带的arguments对象。
  3. 自定义的具有length等满足要求的对象。
var fakeArr = {
	name: 'hyt',
	hobby: 'badminton',
	length: 2
}
//检测伪数组的方式:
fakeArr instanceof Array; false;
//使伪数组具有数组的方法的方式:
Array.protptye.slice.call(fakeArr);

function中自带的arguments参数也是伪数组,arguments包含了函数中的参数数组,arguments对象中(它确实也是对象)还带有callee属性,callee属性是一个指针,指向拥有这个arguments对象的函数。

我们可以利用arguments下的callee写递归函数(阶乘函数):

function recursive(num) {
		if(num <= 1) {
			return num;
		} else {
			return num * arguments.callee(num-1);
		}
}
//使用arguments.callee代替函数名,降低了耦合性,更加灵活。

简述promise
当我们请求来的数据们需要相关联使用时,我们也许会使用以下这种方式发到目的:

$.ajax({
        data: {
            id: id
         }
       })
      .done(function(response) {
              $.ajax({
		        data: {
		            id: response.id
		         }
		       }).done( function(res){})........
 })

这种多层嵌套方法的写法又叫邪恶金字塔,是十分不友好的,而pormise()就可以避免这种情况。
promise基本概念

  1. promise只有三种状态,未完成,完成(fulfilled)和失败(rejected)。
  2. promise的状态可以由未完成转换成完成,或者未完成转换成失败。
  3. promise的状态转换只发生一次 promise的状态可以由未完成转换成完成,或者未完成转换成失败。

promise有一个then方法,then方法可以接受3个函数作为参数。前两个函数对应promise的两种状态fulfilled, rejected的回调函数。第三个函数用于处理进度信息。

var promise = new Promise();
promise.then(function(){},function(){},function(){})

练习:使用promise()封装一个ajax

function ajaxFn(url,method) {
	var promise = new Promise(function(resolve,reject){
		var xhr = new XMLHttpRequest();//创建一个xhr对象;
		xhr.
		xhr.onreadystatechange = function() {
			if (readystate == 4) {
				if(xhr.status >=200 && xhr.status <300 || xhr.status == 304) {
				//状态码200为成功,304 not modify读取浏览器缓存文件;
						resolve(xhr.response);
				} else {
						reject(xhr.responseText);
				}
			}
		};
		xhr.open(url,method);//打开服务器地址,与服务器交互;
		xhr.responseType = 'json';
		xhr.send(null);
		return promise;
	});
};
ajaxFn('aaa.bb.com','get').then(function(data){
		console.log(data);
},function(errmsg){
		console.error(errmsg);
})
//ajax实现思路:open()启动发送请求的准备,send()发送请求,readyState表示响应过程的当前活动阶段,4代表成功,readyState每变化一次会触发onreadystatechange()事件,所以以利用onreadystatechange事件来监听readystate的状态。为了兼顾浏览器的兼容问题,需要将onreadystatechange事件卸载open()方法之前。

//当发送多个请求中途需要砍掉有些请求时:xhr.abort()方法,可以终止xhr.send()的发送。
//url必须符合同源策略

关于Cookie
众所周知,HTTP 是一个无状态协议,所以客户端每次发出请求时,下一次请求无法得知上一次请求所包含的状态数据,如何能把一个用户的状态数据关联起来呢?

比如在淘宝的某个页面中,你进行了登陆操作。当你跳转到商品页时,服务端如何知道你是已经登陆的状态?

cookie
首先产生了 cookie 这门技术来解决这个问题,cookie 是 http 协议的一部分,它的处理分为如下几步:

服务器向客户端发送 cookie。
通常使用 HTTP 协议规定的 set-cookie 头操作。
规范规定 cookie 的格式为 name = value 格式,且必须包含这部分。
浏览器将 cookie 保存。
每次请求浏览器都会将 cookie 发向服务器。
其他可选的 cookie 参数会影响将 cookie 发送给服务器端的过程,主要有以下几种:

path:表示 cookie 影响到的路径,匹配该路径才发送这个 cookie。
expires 和 maxAge:告诉浏览器这个 cookie 什么时候过期,expires 是 UTC 格式时间,maxAge 是 cookie 多久后过期的相对时间。当不设置这两个选项时,会产生 session cookie,session cookie 是 transient 的,当用户关闭浏览器时,就被清除。一般用来保存 session 的 session_id。
secure:当 secure 值为 true 时,cookie 在 HTTP 中是无效,在 HTTPS 中才有效。
httpOnly:浏览器不允许脚本操作 document.cookie 去更改 cookie。一般情况下都应该设置这个为 true,这样可以避免被 xss 攻击拿到 cookie。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值