1、 JS的变量类型
JS可以识别7种不同类型的值
简单类型:Boolean Number Strng null undefined Symbol (ES6新增,一种数据类型,它的实例是唯一且不可改变的)
复杂类型:Object
两种类型的区别是:存储位置不同;
原始数据类型直接存储在栈(stack)中的简单数据段,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储;
引用数据类型存储在堆(heap)中的对象,占据空间大、大小不固定。如果存储在栈中,将会影响程序运行的性能;
引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体
typeof: number 、string、boolean 、undefined、function 、object ( 对象、数组、null )、symbol ( 7种 )
2、== 和 === 的区别 以及 == 如何隐式转换?
=== 、 !==判别方式:
1、不同类型,直接false
2、原始类型值相同,返回true
3、 复合类型(对象、数组、函数)的数据比较时,看它们是否指向同一个对象
4、undefined 和 null 与自身严格相等
==、!= 判别方式
1、布尔值会在比较之前转换成数值 false-0 true-1
2、 如果一个操作数是字符串,另一个是数值,比较之前将字符串转换成数值
3、如果一个操作数是对象,另一个不是,则调用对象的valueOf()方法,再用基本类型值按照之前的规则进行比较
1 var str2 = new String("http://www.365mini.com");
2 console.log(str2.valueOf() == "http://www.365mini.com")
4、 null 和 undefined 是相等的
5、 比较之前不能将null 和 undefined 转换
6、NaN 不等于任何值,包括他自己
7、如果两个操作数都是对象,则比较它们是不是同一个对象,如果都指向同一个对象,则为true, 否则为false
3、undefined 和 null 的区别
null 用来表示尚未存在的对象,常用来表示函数企图返回一个不存在的对象 ,转换成数值 0。对象原型链的终点。
undefine 表示变量已经声明,但还没赋值的时候 默认值为 undefined,转换成 NaN, 函数没有返回值时,默认返回undefined
4、JS 如何实现继承
总共有6种方法 分为构造函数的继承 和 普通对象的继承
1.原型链继承
缺点:一个实例改变了引用类型的属性,其他实例都会发生改变
1 function Subtype(){ 2 this.age = 2; 3 } 4 function Supertype() { 5 this.name = 'sansa' 6 } 7 Subtype.prototype = new Supertype(); //此时subtype.prototype的已经有了supertype的属性 8 9 var s = new Subtype(); 10 s.name //sansa
2.借用构造函数继承
缺点:函数无法复用,每调用一次都会创建一个函数对象---
1 function Supertype(name){ 2 this.colors = ["red","yellow"]; 3 this.name = name; 4 } 5 6 function Subtype(){ 7 Supertype.call(this,'hehe'); //关键代码 直接在子类型中调用超类型的构造函数 这样只能得到构造函数中的属性,无法连接到原型链 比较粗暴,优点是可以传值 8 } 9 var ss = new Subtype(); 10 11 ss.name //hehe
3.组合继承(原型+借用构造) 缺点:构造函数继承的属性(出现在subtype实例中),在原型链中又会重新继承一次(出现在subtype.protype属性中)
将上面两种结合,构造函数中继承 个性属性(例如数组属性),原型链中继承 函数
4.原型式继承 (
普通对象
继承,ES5中的 Object.create() ) 缺点:和原型链继承一样,数组属性 会被改变--
1 function object(o) { 2 function F(){} 3 F.prototype = o; //返回的是 F 的实例,而F的 prototype 指向的是o , 实例本身没有属性,都是靠原型链继承的-- 4 return new F(); 5 }
这个object()函数,其实只做一件事,就是把子对象的prototype属性,指向父对象,从而使得子对象与父对象连在一起。
5.寄生式继承
function createAnother (original) { var clone = object(original); clone.sayHi = function() { console.log('hi'); } return clone; //挂羊头卖狗肉---- 本质还是原型式继承 }
6.寄生组合式继承
为了解决组合继承的缺点(继承了两次基本属性--) 原理:不必为了指定子类型的原型而调用超类型的构造函数,重新改写 原型链那一段
1 function inheritPrototype(subtype,supertype){ 2 var prototype = object(subpertype.prototype); 3 prototype.construcor = subtype; //prototype 上除了constructor 没有其他属性,唯一的作用就是作为连接桥梁 4 subtype.prototype = prototype; 5 }
顺便看看阮一峰的深拷贝--
浅拷贝只是遍历父元素的属性,然后赋给 子元素 ,如果父元素中有引用类型的属性,子对象拿到的是一个内存地址, 子对象会改变父对象的引用属性
深拷贝:实现真正意义上数组和对象的拷贝。递归调用"浅拷贝
1 function deepCopy(p,c) { 2 var c = c || {}; 3 for(var i in p) { 4 if(typeof p[i] === 'object') { 5 c[i] = (p[i].constructor === Array) ? [] : {}; 6 deepCopy(p[i],c[i]); 7 } else { 8 c[i] = p[i]; 9 } 10 } 11 12 return c; 13 }
5、new 操作符具体干了些什么
1、创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型。
2、执行函数,上下文this 会指定为这个新实例对象
3、如果构造函数返回了一个对象,则这个对象会取代new出来的结果,否则会返回这个实例对象 新创建的对象由 this 所引用,并且最后隐式的返回 this 。
1 var new2 = function(func){ 2 var o = Object.create(func.prototype); 3 var k = func.call(o); 4 if(typeof k === Object) { 5 return k 6 } else { 7 return o; 8 } 9 }
6、 This 用法
this 用法提了很多次,但是对某些点总是有些模糊 (注意:一定要分析真正的调用位置,因为它决定了 this 的绑定);
基本的四种不必再多说 ---
在函数默认绑定中:如果在函数内部加上 'use strict' ,this 会绑定到undefined
隐式绑定: 对象属性只有上一层或者 最后一层 在调用位置中起作用
1 function foo() { 2 console.log(this.a); 3 } 4 var obj2 = { 5 a:52, 6 foo:foo 7 } 8 9 var obj1 = { 10 a:2, 11 obj2:obj2 12 } 13 obj1.obj2.foo() //52 起作用的是 obj2
同样也很有可能会绑定到全局对象中,看到底是谁在调用 例如:
1 var bar = obj2.foo; // obj2 是上面的- 2 var a = 22; 3 bar(); //22
setTimeout(obj.foo,100) 也是会绑定到全局对象
显示绑定:
优先级: new > 显式 > 隐式 > 默认
箭头函数:本身没有 arguments、this,与外层的函数保持一致 因为没有 this, apply.call.bind等方法都无效, 当然也不能做构造函数---
7、创建对象
马克飞象上面已经有写的比较详细了--所以就懒得再写了
8、JS 的垃圾回收机制
8.1 标记清除(最常用的垃圾收集方式)
当变量进入环境时,将这个变量标记为“进入环境”,离开环境时,将其标记为 离开环境。先给存储在内存中的所有变量都加上标记,然后删除处在环境中的变量以及被环境变量引用的变量的标记,剩下的就是无法再访问的变量,销毁那些带标记的值并回收
它们的内存空间--
8.2 引用计数 跟踪记录每个值被引用的次数
当声明了一个变量 被将一个引用类型赋给该变量时,这个值得引用次数就是1 ,变量被赋给另一个值时,这个引用类型就要减1,当为0 的时候即没有办法再访问这个值,则回收内存空间
9、JS 合并后与合并前哪个快
多数浏览器使用单一进程来处理JS脚本执行,所以在解析和执行脚本时,浏览器会停止处理页面.
在script 标签里面加上 defer 或者是 async ,会异步并行加载脚本,下载时不会阻塞脚本
defer 是在等待页面完成之后执行, async 是立刻执行