js笔记
1.原型链
image.png2.闭包
闭包就是一个函数引用另外一个函数的变量,因为变量被引用着所以不会被回收,因此可以用来封装一个私有变量。这是优点也是缺点,不必要的闭包只会徒增内存消耗!
有权访问另一个函数作用域内变量的函数都是闭包。这里 inc 函数访问了构造函数 a 里面的变量 n,所以形成了一个闭包。
function a(){
var n = 0;
function inc(){
n++;
console.log(n);
}
return inc;
}
var c = a();
c(); //1
c(); //2
3.js三种继承
4.函数柯里化
function add(){
var sum = 0;
if(arguments.length > 1){
for (var i = 0; i < arguments.length; i++) {
sum += arguments[i];
}
return sum;
} else {
sum+= arguments[0];
}
var tempFun = function(numB){
if(arguments.length===0){
return sum;
}else{
sum= sum+ numB;
return tempFun;
}
}
tempFun.valueOf=function(){
return sum;
}
return tempFun;
}
console.log(add(3, 4)) //7
console.log(add(3)(4).valueOf()) //7
5.bind实现
6.与非或,string与number转换
console.log(1 + '2'); // 12
console.log(1 ^ 2); // 3 位异或(^)
console.log(1 ^ '2'); // 3 000001 000010 000011 11=0 10=1 01=1 00=0
console.log(1 | 2); // 3 位或(|) 000001 000010 11=1 10=1 01=1 00=0
console.log(~ 2); // -3 (位非~)位非运算实际上就是对数字进行取负运算,再减 1
console.log(1 << 2); // 4 01 000001 000100 左移2位
console.log(1 >> 2); // 0 01 000001 000000 右移2位
console.log(1 << '2'); // 4 01 000001 000100 左移2位
console.log(1 >> '2'); // 0 01 000001 000000 右移2位
7.变量作用域
var foo = 'Hello',
bar;
(function() {
var bar = ' World';
console.log(foo + bar); //Hello World
})();
console.log(foo + bar); //Helloundefined
8.null与undefined的区别
1. 相同点
if 判断语句中,两者都会被转换为false
if(null) {
console.log(true)
} else{
console.log(false) //false
}
if(undefined) {
console.log(true)
} else{
console.log(false) //false
}
2. 不同点
Number转换的值不同
console.log(Number(null)) // 0
console.log(Number(undefined)) // NaN
null表示一个值被定义了,但是这个值是空值
undefined表示缺少值,即此处应该有值,但是还没有定义
作为函数的参数,表示函数的参数不是对象
作为对象原型链的终点 (Object.getPrototypeOf(Object.prototype))
定义一个值为null是合理的,但定义为undefined不合理(var name = null)
var i;
console.log(i) // undefined 变量被声明了还没有赋值,就为undefined
function f(x){console.log(x)}
f() // undefined 调用函数时应该提供的参数还没有提供,该参数就等于undefined
var o = new Object();
console.log(o.p)// undefined 对象没有赋值的属性,该属性的值就等于undefined
var x = f();
console.log(x) // undefined 函数没有返回值,默认返回undefined
9.使用new和不使用new的差别
不使用new,也就是普通的函数调用而已,所以若是函数本身没有返回值,普通的函数调用没有什么意义。
var person=new Person();//person是一个对象
var person = Person();//这只是一次普通的函数调用并赋值而已。
function Person(name, age) {
this.name = name;
this.age = age;
this.sayName = function() {
alert(this.name);
};
}
//var person=new Person("张三",20); //此处为 构造对象,构造对象的话,返回的新对象是由解析器自己生成的。
var person = Person('张三', 20); //假设我在Person函数里面加了return "你好"; 这时的person就不会报undefined,而是一个字符串你好
// person.sayName(); //报错 person undefined 此处为普通函数调用,又没有给定返回值,出错。
//因为此时this指向window对象,
window.sayName(); //此时不会报错
// 接下来就问,为什么我赋值给person,可以用window来引用呢?
// 因为如果不用new就相当于普通函数调用,而 Person()根本没有返回值,
// 所以Person根本就没赋值给person,此时的person只是一个undefined,
// 但是Person却执行了一次,成为了window的对象,this指向了window,所以window可以直接使用Person的方法。
Person('张三', 20);
person.sayName(); //报错 person undefined 此处为普通函数调用,又没有给定返回值,出错。
检测a的原型链(proto)上是否有B.prototype,若有返回true,否则false。
10.typeof
typeof用以获取一个变量或者表达式的类型,typeof一般只能返回如下几个结果:
number,boolean,string,function(函数),object(NULL, 数组,对象),undefined。
11.instanceof
正因为typeof遇到null,数组,对象时都会返回object类型,所以当我们要判断一个对象是否是数组时
或者判断某个变量是否是某个对象的实例则要选择使用另一个关键语法instanceof
function instance_of(L, R) {//L 表示左表达式,R 表示右表达式
var O = R.prototype; // 取 R 的显示原型
L = L.__proto__; // 取 L 的隐式原型
while (true) {
if (L === null)
return false;
if (O === L) // 当 O 显式原型 严格等于 L隐式原型 时,返回true
return true;
L = L.__proto__;
}
}
instanceof 操作符用来比较两个操作数的构造函数。只有在比较自定义的对象时才有意义。 如果用来比较内置类型,将会和 typeof 操作符 一样用处不大。
12.https
image.png1. 客户端请求建立SSL连接,并将自己支持的一套加密规则发送给网站。
2. 网站从中选出一组加密算法与HASH算法,并将自己的身份信息以证书的形式发回给浏览器。证书里面包含了网站地址,加密公钥,以及证书的颁发机构等信息
3. 获得网站证书之后浏览器要做以下工作:
Ø 验证证书的合法性
Ø 如果证书受信任,浏览器会生成一串随机数的密码,并用证书中提供的公钥加密。
Ø 使用约定好的HASH计算握手消息,
Ø 使用生成的随机数对消息进行加密,最后将之前生成的所有信息发送给网站。
4. 网站接收浏览器发来的数据之后要做以下的操作:
Ø 使用自己的私钥将信息解密取出密码
Ø 使用密码解密浏览器发来的握手消息,并验证HASH是否与浏览器发来的一致。
Ø 使用密码加密一段握手消息,发送给浏览器
5. 浏览器解密并计算握手消息的HASH,如果与服务端发来的HASH一致,此时握手结束。
6. 使用随机密码和对称加密算法对传输的数据加密,传输。
13.https 加密与HASH算法如下:
1.非对称加密算法:RSA,DSA/DSS,用于在握手过程中加密生成的密码。
2.对称加密算法:AES,RC4,3DES,用于对真正传输的数据进行加密。
3.HASH算法:MD5,SHA1,SHA256,验证数据的完整性。
14.HTTP与HTTPS的区别:
1.https协议需要申请证书。
2.http是超文本传输协议,明文传输;https使用的是具有安全性的SSL加密传输协议。
3.http端口80;https端口443。
4.http连接简单无状态;https由SSL+HTTP协议构件的可进行加密传输、身份验证的网络协议
HTTPS协议工作流程
15.HTTP的几种请求方法用途
1)GET方法
发送一个请求来取得服务器上的某一资源
2)POST方法
向URL指定的资源提交数据或附加新的数据
3)PUT方法
跟POST方法很像,也是想服务器提交数据。但是,它们之间有不同。PUT指定了资源在服务器上的位置,而POST没有
4)HEAD方法
只请求页面的首部
5)DELETE方法
删除服务器上的某资源
6)OPTIONS方法
它用于获取当前URL所支持的方法。如果请求成功,会有一个Allow的头包含类似“GET,POST”这样的信息
7)TRACE方法
TRACE方法被用于激发一个远程的,应用层的请求消息回路
8)CONNECT方法
把请求连接转换到透明的TCP/IP通道
HTTP的几种请求方法用途
16.promise.then,process.nextTick, setTimeout 以及 setImmediate 的执行顺序
setImmediate(function(){
console.log(1);
},0);
setTimeout(function(){
console.log(2);
},0);
new Promise(function(resolve){
console.log(3);
resolve();
console.log(4);
}).then(function(){
console.log(5);
});
console.log(6);
process.nextTick(function(){
console.log(7);
});
console.log(8);
// 3 4 6 8 7 5 2 1
观察者优先级
在每次轮训检查中,各观察者的优先级分别是:
idle观察者 > I/O观察者 > check观察者。
idle观察者:process.nextTick
I/O观察者:一般性的I/O回调,如网络,文件,数据库I/O等
check观察者:setTimeout>setImmediate
同步代码执行顺序优先级高于异步代码执行顺序优先级;
new Promise(fn)中的fn是同步执行;
process.nextTick()>Promise.then()>setTimeout>setImmediate。
promise.then,process.nextTick, setTimeout 以及 setImmediate 的执行顺序
17.setTimeout async promise执行顺序
async function async1() {
console.log("async1 start");// 同步代码2
await async2();// 调用async2(),async2()的返回值是promise,不执行promise的resolve,让出线程
console.log("async1 end");
}
async function async2() {
console.log("async2");// 同步代码3
}
console.log("script start");// 同步代码1
setTimeout(function() {
// 异步setTimeout放入event-loop中的macro-tasks队列,暂不执行
console.log("setTimeout");
}, 0);
async1();
new Promise(function(resolve) {
console.log("promise1");// 同步代码4
resolve();
}).then(function() {
console.log("promise end");// 不执行
});
console.log("script end");// 同步代码5
/**
script start
async1 start
async2
promise1
script end
promise end
async1 end
setTimeout
*/
console.log("script start"); // 同步代码1这句代码毫无疑问是同步执行的 ;
setTimeout()是异步任务,加入宏任务队列,不执行;
然后调用async1(),执行这个方法体内的同步函数,打印console.log("async1 start"); // 同步代码2;
向下执行,遇到await关键字,调用async2(),执行同步代码打印console.log("async2"); // 同步代码3,让出线程。await是让出当前函数线程,交给函数外的代码执行;
线程跳出async1(),向下执行Promise(),执行里面的同步代码打印promise1,resolve是异步函数,加入微任务队列,此时继续执行同步函数,回到await关键字处,执行剩余代码;
此时没有同步任务,就去执行微任务队列任务,所以会优先执行promise队列。
此时微队列任务顺序:new Promise().resolve()- console.log("async1 end");,s所以先执行下一个,打印 console.log("promise end")`;
这时候同步向下执行console.log("async1 end");
最后执行宏任务setTimeout()。
深入理解setTimeout async promise执行顺序
18.import和require的区别
node编程中最重要的思想就是模块化,import和require都是被模块化所使用。
遵循规范
require 是 AMD规范引入方式
import是es6的一个语法标准,如果要兼容浏览器的话必须转化成es5的语法
调用时间
require是运行时调用,所以require理论上可以运用在代码的任何地方
import是编译时调用,所以必须放在文件开头
本质
require是赋值过程,其实require的结果就是对象、数字、字符串、函数等,再把require的结果赋值给某个变量
import是解构过程,但是目前所有的引擎都还没有实现import,我们在node中使用babel支持ES6,也仅仅是将ES6转码为ES5再执行,import语法会被转码为require
import和require的区别
19.箭头函数
箭头函数this为父作用域的this,不是调用时的this
箭头函数不能作为构造函数,不能使用new
箭头函数没有arguments,caller,callee
箭头函数通过call和apply调用,不会改变this指向,只会传入参数
箭头函数没有原型属性
箭头函数不能作为Generator函数,不能使用yield关键字
箭头函数返回对象时,要加一个小括号
箭头函数在ES6 class中声明的方法为实例方法,不是原型方法
多重箭头函数就是一个高阶函数,相当于内嵌函数
箭头函数常见错误
let a = {
foo: 1,
bar: () => console.log(this.foo)
}
a.bar() //undefined
20.如何将arguments类数组变为数组
arguments是一个对象,但是拥有length属性
但是arguments并没有slice方法
那么考虑用apply或者call改变Array.prototype.slice()方法的this,将该方法的this指向arguments,利用
Array.prototype.slice.apply(arguments)
var unboundSlice = Array.prototype.slice;
var slice = Function.prototype.call.bind(unboundSlice);
function list() {
return slice(arguments);
}
var list1 = list(1, 2, 3); // [1, 2, 3]
21.中序遍历
// 递归
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @return {number[]}
*/
var inorderTraversal = function(root) {
var result = [];
function pushRoot(root){
if(root == null){
return true;
}
if(pushRoot(root.left)){
result.push(root.val);
return pushRoot(root.right)
}
}
pushRoot(root)
return result;
};
// 栈:后进先出 中序遍历方向从左到右遍历,当前根节点位于中间【左-根-右】
var inorderTraversal = function(root) {
var result = [];
var tmpStack = [];
var currNode = root;
while(currNode != null || tmpStack.length !=0){
while(currNode != null){
tmpStack.push(currNode);
currNode = currNode.left;
}
currNode = tmpStack.pop();
result.push(currNode.val);
currNode = currNode.right;
}
return result;
};
21.隐式转换
22.数字千位分割
1.toLocaleString()
2.循环倒序
function numFormat(num) {
num = num.toString().split('.'); // 分隔小数点
var arr = num[0].split('').reverse(); // 转换成字符数组并且倒序排列
var res = [];
for (var i = 0, len = arr.length; i < len; i++) {
if (i % 3 === 0 && i !== 0) {
res.push(','); // 添加分隔符
}
res.push(arr[i]);
}
res.reverse(); // 再次倒序成为正确的顺序
if (num[1]) {
// 如果有小数的话添加小数部分
res = res.join('').concat('.' + num[1]);
} else {
res = res.join('');
}
return res;
}
3.正则
function numFormat(num) {
var res = num.toString().replace(/\d+/, function(n) {
// 先提取整数部分
return n.replace(/(\d)(?=(\d{3})+$)/g, function($1) {
return $1 + ',';
});
});
return res;
}
var a = 1234567894532;
var b = 673439.4542;
console.log(numFormat(a)); // "1,234,567,894,532"
console.log(numFormat(b)); // "673,439.4542"