-
JavaScript 基本数据类型
-
string 字符串 -
number 数字 -
boolean 布尔值 -
Symbol 独一无二的值,创建之后就不能改了 -
null 空值 null === undefined // true -
undefined 未定义
-
JavaScript 引用数据类型
-
object 对象 -
array 数组
-
JavaScript 判断数据类型的四种方法
-
typeof
-
(个人理解,用来判断基本数据类型的时候,比较好使,但是,对 null 就不太行了,得有沉淀才行。)
typeof ''; // string 有效
typeof 1; // number 有效
typeof Symbol(); // symbol 有效
typeof true; //boolean 有效
typeof undefined; //undefined 有效
typeof null; //object 无效
typeof [] ; //object 无效
typeof new Function(); // function 有效
typeof new Date(); //object 无效
typeof new RegExp(); //object 无效
-
对于基本数据类型,除了 null 以外,其他返回的结果都是正确的; -
对于引用数据类型,出 function 以外,一律返回 object 类型; -
对于 null 返回的是object 类型; -
对于 function 返回的是,function 类型;
-
instanceof
-
insanceof 用老用来判断A是否是B的实例,语法 a instanceof b 。如果是,返回 true;否则返回 false ;注意:这里的话, instanceof 检测的是原型,(适合检测引用数据类型) -
???????instanceof (C,D) = {
? ?// C,D 的属性,是 JS 规定了,当进行传参的时候,都会默认将 原型(prototype) 传进行来
? ?var L = C.__proto__;
? ?var R = D.prototype;
? ?if(L === R) {
? ? ? ?// C的内部属性 __proto__ 指向 D 的原型对象
? ? ? ?return true;
? }
? ?return false;
} -
[] instanceof Array; // true
{} instanceof Object;// true
new Date() instanceof Date;// true
function Person(){}; ?// 构造函数
new Person() instanceof Person; // true
[] instanceof Object; // true
new Date() instanceof Object;// true
new Person instanceof Object;// true -
分析一下 对 [ ],Array,Object,三者之间原型链的关系
-
可以看出 instanceof 判断出来的 [ ].proto 指向 Array.prototype,然后,Array.prototype.proto又指向 Object.prototype,最终Object.prototype.proto 指向了 null ,也就是原型链的最高点,终点了;可以看出,[ ], Array, Object 在内部形成了 一个原型链,因此看来可以判断出他们对方的实例; -
-
注意:instanceof 只能用来判断两个对象是否属于 实例的关系。而不能判断一个对象实例具体属于那种类型;
-
constructor
-
当一个函数 A 被定义的时候,JS 解析引擎会给 A 添加 prototype 原型,然后再给 prototype 上添加一个 constructor 属性,并让其指向 A 的引用。 ? -
当构造函数实例化一个对象的时候,B 是 A 的实例对象,此时 A 原型上的 constructor 传递到 B 上,因此 B.constructor === A 是 true。
-
分析:A 利用原型对象上的 constructor 引用了自身,当 A 作为一个构造函数来创建一个对象的时候,原型上的 constructor 就被遗传到了新创建的对象上;从原型链角度上看,构造函数 A 就是新对象的类型,这种做法的作用是,让新对象在诞生时候,就有可追溯的数据类型。 -
???????
-
toString
-
toString() 是 Object 原型方法,调用该方法的时候,默认返回的当前对象的 类,这是一个内部属性,格式为 [object Xxx] ,其中 Xxx 就是对象的类型。 -
判断 Object 对象,直接调用 toString 方法,就可以直接返回 【object Object】。对于其他的对象,则需要通过, call/apply 来调用才能返回正确的类型信息。 -
Object.prototype.toString.call('') ; ? // [object String]
Object.prototype.toString.call(1) ; ? // [object Number]
Object.prototype.toString.call(true) ; // [object Boolean]
Object.prototype.toString.call(Symbol()); //[object Symbol]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
Object.prototype.toString.call(new Error()) ; // [object Error]
Object.prototype.toString.call(document) ; // [object HTMLDocument] -
-
JavaScript 预解析 和 代码 执行
-
预解析
-
在这个过程中,会有 参数声明、函数提升( 函数声明 )、变量提升( var )、查明 this、等工作;
-
代码执行
-
代码执行输出结果;、
-
JavaScript 原型 与 原型链
-
原型 function People() {}
var people1 = new People()
-
每一个函数都有一个 prototype 属性,属性指向 一个对象,这个对象就是,构造函数创建的实例的原型,也就就是 people1 的原型。原型:就是每个JavaScript 对象1(除了null)在创建的时候,都会关联的另一个对象,这个对象就是原型,每一个对象1会从原型上继承属性。也就是 __ proto __ -
每一个Javascript(除null)都有一个 属性叫 __ proto __ 这个属性指向 改 对象的原型,由这些指向,后期发展成 原型链。 -
function People() {}
var plepeo = new People();
console.log(plepeo.__proto__ === People.prototype); // true
-
细节:实例对象和构造函数都可以指向 原型。 -
当读取一个实例的属性时,如果实例没有 ,就会通过 :实例.__ proto __ 的指向 ,向上查找构造函数的原型上: People.prototype 的属性,如果还是没有没有,就会原型的 原型 (People.prototype.__ proto __ ) 继续查找,一直查找到最顶层 null ,这也就是 原型链
-
原型链
-
console.log(
? ?Object.prototype.__proto__ === null ?// true
) -
null 即表示 此处没有对象,也就是没有 值;所以,当 Object.prototype.__ proto __ 的值为 null 的时候跟 Object.prototype 没有原型 是一样的,也就是一个意思哈!然后就可以停止查找了。
-
JavaScript 的this 指向
-
this 的指向,在 函数 创建的时候时决定不了的,在 最终 调用的时候才能决定,谁调用就指向谁; -
注意:
-
场景一:如果一个函数中有 this ,但是没有它没有被上一级的对象调用,那么 this 的指向就是 window ; -
场景二: 如果一个函数中有 this ,这个函数有被上一级的对象调用,那么 this 指向就是 这个上一级的对象; -
如果函数中有 this ,且这个函数中包含多个对象,尽管这个函数是被 最外层的对象调用,但是 this 的指向也只是它的上一层级的对象(o.b.fn() 这种,this 的指向时 b)
var o = {
? ?a:10,
? ?b:{
? ? ? ?// a:12,
? ? ? ?fn:function(){
? ? ? ? ? ?console.log(this.a); //undefined
? ? ? }
? }
}
o.b.fn(); // 函数执行 -
特殊例子
-
var o = {
? ?a:10,
? ?b:{
? ? ? ?a:12,
? ? ? ?fn:function(){
? ? ? ? ? ?console.log(this.a); //undefined window.a 肯定是 undefined的
? ? ? ? ? ?console.log(this); //window
? ? ? }
? }
}
var j = o.b.fn;
j();
// 这里的话,可以这样理解的,this 的指向永远指向 最后调用它的对象,也就是看它指向的时候被谁调用了,这个的话,是将 fn 赋值给了 j 但是没有执行,所以 this 的指向是 widow
-
构造函数的 this 指向
-
function Fn(){
? ?this.user = "清云";
}
var a = new Fn();
console.log(a.user); //清云 -
这里 this 指向为什么是 a 呢?
-
因为:new 关键字 在创建一个实例(对象)的时候,调用 apply (不一定是 apply)改变了 this 指向,将 this 指向这个空对象,将实例的 this 替换掉,其实就是相当与 复制了一份 Fn 到 a 对象里面,然后改变 这个 复制函数的 this 指向,指向自己。
-
this 遇到 return 的时候(面试一般不问)
-
function fn() { ?
? ?this.user = '清云'; ?
? ?return {}; ?
}
var a = new fn; ?
console.log(a.user); // undefined -
function fn() { ?
? ?this.user = '清云'; ?
? ?return function() {};
}
var a = new fn; ?
console.log(a.user); // undefined -
function fn() { ?
? ?this.user = '清云'; ?
? ?return 1;
}
var a = new fn; ?
console.log(a.user); // 清云 -
function fn() { ?
? ?this.user = '清云'; ?
? ?return undefined;
}
var a = new fn; ?
console.log(a.user); // 清云 -
function fn() { ?
? ?this.user = '清云'; ?
? ?return undefined;
}
var a = new fn; ?
console.log(a); // fn { user: "清云" } -
function fn() { ?
? ?this.user = '清云'; ?
? ?return null;
}
var a = new fn; ?
console.log(a.user); // 清云 -
总结: 如果返回的是一个对象,那么 this 指向就是返回的 那个对象,如果返回值不是一个对象,那么 this 指向还是 函数的实例;null 也是对象,比较特殊,但是返回的也是 this 还是指向函数的实例对象;返回 数值 还有 布尔 类型的,this 的指向还是实例化的对象。
-
JavaScript 的闭包
-
闭包的特性
-
函数嵌套函数; -
函数内部可以引用函数外部的变量和参数; -
变量和参数不会被垃圾回收机制回收; -
function a() {
var name = '清云';
? ?return function () {
? return name
? }
}
var b = a()
console.log(b()); // 清云 -
function a() {
var num = 0;
? ?return function () {
? ? ? ?var n = 0;
? ? ? ?console.log(++num);
? ? ? ?console.log(++n);
? }
}
var fn1 = a()
fn1(); // 1, 1
fn1(); // 1, 2
fn1 = null // 销毁闭包
-
优点:
-
实现了封装代码,防止变量流入其他的环境中; -
在内存中维持了一个变量,不会被销毁;
-
坏处:
-
造成内存泄露; -
变量不能被销毁; -
执行速度变慢;
-
销毁闭包
-
在运行完了之后给 变量进行赋值 为 null 为空; -
等待页面的关闭,才会被销毁;
-
JavaScript 递归
-
概念:其实就是自己 调用 自己 -
例子 // 求 1-100 的累加和
function sum(n){
? ?if(n === 1) return 1 // 结束条件,避免一直循环下去;
? ?return sum(n-1) + n;
}
sum(100);
-
JavaScript const、let、var 的区别
-
const 定义的基本数据类型的变量不可修改,而且必须初始化;但是是引用数据类型的话,这个变量保存的是指针,所以可以更改这个对象的值,但是保存的这个指针不能改变; -
let 是块级作用域,函数内部使用 let 之后,对函数外部没有影响,同时不能跨函数访问,不能跨块级访问: -
var 定义的变量可以修改值,如果如果不初始化的话,会输出 undefined ,不会报错;能跨函数使用,跨块级访问; -
JavaScript 中的作用域
-
全局作用域(ES5新增): 声明在函数外部的变量 -
局部作用域(ES5新增): 函数中声明 -
块级作用域(ES6新增):一个函数,一个花括号包裹就行,就是块级作用域 -
JavaScript 深拷贝和浅拷贝
-
递归
-
function deepClone(obj){
? ?let objClone = Array.isArray(obj)?[]:{};
? ?if(obj && typeof obj==="object"){
? ? ? ?for(key in obj){
? ? ? ? ? ?if(obj.hasOwnProperty(key)){
? ? ? ? ? ? ? ?// 是否有多层,如果是,递归复制
? ? ? ? ? ? ? ?if(obj[key]&&typeof obj[key] ==="object"){
? ? ? ? ? ? ? ? ? ?objClone[key] = deepClone(obj[key]);
? ? ? ? ? ? ? }else{
? ? ? ? ? ? ? ? ? ?// 只有一层的情况
? ? ? ? ? ? ? ? ? ?objClone[key] = obj[key];
? ? ? ? ? ? ? }
? ? ? ? ? }
? ? ? }
? }
? ?return objClone;
} ? ?
? ?let a=[1,2,3,4],
? ?b=deepClone(a);1
? ?a[0]=2;
? ?console.log(a,b);
-
JSON.parse( ) 还有 JSON.stringify( ) -
事件循环 event Loop
-
由来:JavaScript 的设计之初,便是 单线程 的,同一时间 只能 做一件事; JavaScript 初期作为一种浏览器语言,通常被用来操作 DOM ,如果是多线程的话,一个线程删除了 DOM 另一个删除 DOM ; 这时,浏览就不能处理了;为了解决这种 单线程阻塞问题,JavaScript 用到了,计算机的一种运行机制,这种机制就是 事件循环;
?在 JavaScript 中,所有的任务都可以分为 -
-
同步任务:立即执行的任务,同步任务一般会直接进入到主线程中执行; -
异步任务:异步执行,比如 Ajax 网络请求,setTimeout 定时器函数等; -
同步任务与异步任务的运行流程
? -
从图中可以看出,同步任务进入主线程中 ,即是主执行栈,异步任务进行任务队列,主线程内的任务执行完毕之后为空,就会去任务队列中读取相应的任务,推入 主线程执行,这个过程不断反复,就是事件循环 -
微任务
-
Promise.then -
await 函数中后面的代码,但是在 await 前面的是 同步任务 -
MutaionObserver -
Object.observe(已废弃;Proxy 对象替代) -
process.nextTick(Node.js)
-
宏任务
-
script (可以理解为外层同步代码) -
setTimeout/setInterval -
UI rendering/UI事件 -
postMessage、MessageChannel -
setImmediate、I/O(Node.js)
-
async function async1() {
? ?console.log('async1 start')
? ?await async2()
? ?console.log('async1 end')
}
async function async2() {
? ?console.log('async2')
}
console.log('script start')
setTimeout(function () {
? ?console.log('settimeout')
})
async1()
new Promise(function (resolve) {
? ?console.log('promise1')
? ?resolve()
}).then(function () {
? ?console.log('promise2')
})
console.log('script end')
// 所以最后的结果是:script start、async1 start、async2、promise1、script end、async1 end、promise2、settimeout -
注意 微任务 的执行顺序优先于 宏任务
-
JavaScript 异步编程的四种方法
-
回调函数: -
事件监听: -
订阅/发布: -
Promise 对象:创建一个异步对象的话,是通过 async function 创建的;
-
JavaScript 事件流
-
当对网页进行某种操作的时候,就会产生事件,这个事件在一个节点产生 ,然后事件会在元素的节点之间按照特定的顺序传播,所经过的路径的节点都会收到这个事件,这个传播的过程就是 事件流;
? -
阻止冒泡事件
-
event.stopPropagation( ):w3c 模型里,在最里面的事件加上就不会触发父元素的方法执行; -
window.event.cancelBubble = true:微软的模型里,用这个 -
event.target:事件对象;
-
阻止捕获事件
-
stopPropagation(): -
stopImmediatePropagation();
-
DOM 事件流
-
DOM2 级事件流包括三个阶段
-
事件捕获阶段; -
处于目标阶段; -
事件冒泡阶段;
-
JavaScript 改变 this 指向的三个方法
-
call、apply、bind: 前两者是立即调用,后 bind 是稍后调用 -
f1.call(obj,10,20) ?// 此时的this,指向就变成了第一个参数obj,从第二个参数开始,就是函数f1的形参
f1.apply(obj,[10,20]); // 第二个参数是一个数组,它自己会把数组摊开,按照下标作为实参传给对应函数的形参
f1.bind(obj,10,20) // 不会立即调用f1函数 ,而是会产生一个修改了this指向的新的函数
-
JavaScript Promsie(ES6) 和 async/await (ES7)的区别
-
Promise 一共有三个状态
-
pending(执行中)、fullfill(成功)、rejected(失败)
-
async/await
-
async/await 与Promise 是一样的,是非堵塞的;它就是基于Promise 实现的,也就是改良版的Promise ;
-
两者的区别
-
promise是ES6,async/await是ES7 -
async/await相对于promise来讲,写法更加优雅(总结有点肤浅了,小伙伴们可以借鉴,借题发挥哈)
-
还在更新,别急,根据自己的经验总结出来的,啊哈!!!
|