持续不断更新。。。
基本类型和引用类型
vue props | Primitive vs Reference Types
基本类型和字面值之间的区别
基本类型和字面值相等,对象实例和字面值不相等(可用于快速区分基本类型和对象实例)
arguments与类数组对象
看着这篇
https://github.com/mqyqingfen...
隐式转换
包装对象
number string boolean这三种原始类型都有对应的包装类型。
str 是个基本类型 是个字符串类型 strObj是个对象类型,并且这个对象类型实际上是string类型对应的包装类
str是一个基本类型,它不是对象,所以不应该有属性和方法,那么我想访问这个基本类型的字符串,它的长度是多少
这就有些奇怪了,它是一个基本类型,怎么会有这样一个.length属性
我们尝试给它像属性一样赋值,赋值是成功的,但是str.t却是undefined
实际上,javascript中有一个隐藏的机制,当string number boolean这三个基本类型被尝试用对象一样使用的时候,比如访问它的length属性或者给它增加一些属性,当做这样的操作时候,javascript会将这些基本类型转化为对应的包装类型对象相当于
new string()
new number()
当完成这样的访问以后,这个临时的包装对象会被销毁掉,所以在去访问a.t值是undefined
类型检测
杂类
- 判断对象为 null 或者 undefined
// `null == undefined` 为true
if (variable == null) {
// code
}
- 判断对象是否有某个属性
if(myObj.hasOwnProperty("<property name>")){
alert("yes, i have that property");
}
// 或者
if("<property name>" in myObj) {
alert("yes, i have that property");
}
类型检测
检测类型的方法有很多:
- typeof
- instanceof
- Object.prototype.toString
- constructor
- duck type
typeof
运算符会返回一个字符串,非常适合函数对象和基本类型的判断
检测对象类型
常用 instanceof
,它是基于原型链判断的一个操作符
instanceof
左操作数是个对象,如果不是直接返回false
右操作数必须是函数对象或者函数构造器,如果不是会抛出type error
大概原理:instanceof
会判断左操作数上的对象的原型链上是否有右边这个构造函数的prototype
属性
测试某个值是不是原生数组、原生函数或正则表达式
function isArray(value) {
return Object.prototype.toString.call(value) == "[object Array]";
}
function isFunction(value) {
return Object.prototype.toString.call(value) == "[object Function]";
}
function isRegExp(value) {
return Object.prototype.toString.call(value) == "[object RegExp]";
}
检测原生JSON对象
Object的toString()
方法不能检测非原生构造函数的构造函数名。因此,开发人员定义的任何构造函数都将返回[object Object]。有些javascript库会包含类似代码:
var isNativeJSON = window.JSON && Object.prototype.toString.call(JSON) == "[Object JSON]"
在web开发中能够区分原生非原生javascript对象非常重要。只有这样才能确切知道某个对象到底有哪些功能。
实例1
js 中对象是引用传递, 基础类型是值传递, 通过将基础类型包装 (boxing) 可以以引用的方式传递
var a = 3;
a = a * 2;
console.log(a); // a = 6
var b = 1, c =2, d = 3;
var arr1 = [b,c,d];
arr1.forEach((item)=>{
item = item * 2;
});
console.log(arr1); //arr1 = [1,2,3];
var arr2 = [{a:1},{a:2},{a:3}];
arr2.forEach((item)=>{
item.a = item.a*2;
});
console.log(arr2); //arr2 = [{a:2},{a:4},{a:6}]
function changeStuff(a, b, c)
{
a = a * 10;
b.item = "changed";
c = {item: "changed"};
}
var num = 10;
var obj1 = {item: "unchanged"};
var obj2 = {item: "unchanged"};
changeStuff(num, obj1, obj2);
console.log(num);
console.log(obj1.item);
console.log(obj2.item);
//10
//changed
//unchanged
表达式和运算符
var obj = new Foo()
得到一个构造器的实例obj.x
从构造器的prototype属性上拿到xobj.hasOwnProperty('x')
判断这个属性到底是这个对象上的还是对象的原型链上的
通过__proto__
可以拿到对象的原型,这里这个x是属于对象原型上的属性
语句
块block
注意:es6之前没有块级作用域,有全局作用域和函数作用域,还有eval()作用域
函数作用域
浅谈 js eval作用域
这个代码得到的是 1 而不是 123
如果想让 eval 执行的代码是全局的,那么有几种方法。
这个方法标准浏览器都可以得到 123 而IE6-8则依然是 1
ps: 不推荐使用eval()
申明语句
try catch语句
函数和作用域
函数的返回值是依赖return语句的,如果没有return语句会在代码执行完后返回undefined,这是一般的函数调用
如果是作为构造器,然后外部使用new调用,构造器如果没有return语句或者return的是基本类型的话,会将this作为返回
this
JavaScript this
this它本身代表函数运行时自生成的一个内部对象,只能在函数内部使用 ,随着函数使用场合的不同,this的值会发生变化。
但是有个总原则:
this指的是调用函数的那个对象
函数在不同的调用下this指向是不一样的
调用函数对象的apply/call/bind等方法,其作用是改变函数的调用对象,他们的第一个参数就表示改变后的 调用这个函数的对象,this指的就是这第一个参数
bind()方法是es5提供的,所以ie9+才有
arguments
arguments是一个类数组的对象,之所以说类数组,是它的原型并不是array.prototype
所以它没有join()
slice()
这些数组对象才有的方法
严格模式下 arguments.callee
是禁止使用的
apply/call方法(浏览器)
call()第一个参数传的是想作为this的对象,像上面这样如果不是对象,会转成对象,所以这里的100会被转换成对应的包装类 Number(100)
bind方法
- 通过
对象.属性名
这种调用方式(moule.getX()
),如果没有bind影响 这里会返回81 - 我们把
moule.getX
这样的函数对象赋值给getX变量,这样调用this会指向全局对象 所以这里返回9 - 通过bind()方法可以改变函数运行时候里面对应的this,这里的this再绑定module对象以后,再调用 结果就是81了
bind有函数柯里化功能
函数柯里化:把一个函数拆分成多个单元
作用域
不同调用方式
不同创建方式
函数申明 vs 函数表达式
构造器
闭包
什么是闭包
一般函数
function outer () {
var localVal = 30;
return localVal;
}
outer();//30
对于一般的函数,当函数结束调用之后,它的局部变量就可以被释放了,具体释放要取决于垃圾回收的机制
function outer () {
var localVal = 30;
return function () {
return localVal;
}
}
var func = outer();
func(); //30
对于一般函数,函数调用返回的时候,局部变量localval就会被释放了,对于下面这种闭包,localVal是不能被释放的。因为调用outer()以后返回的是一个匿名函数,这个匿名函数里面仍然可以访问外面局部变量 localVal.这种机制就有了闭包的另一个重要的应用场景:让变量值始终保持在内存里(详情往下看)
闭包应该场景
读取函数内部变量
让变量值始终保持在内存里
(延长局部变量的生命周期,封装私有变量)
img 对象经常用于进行数据上报,如下所示:
var report = function( src ){
var img = new Image();
img.src = src;
};
report( 'http://xxx.com/getUserInfo' );
但是通过查询后台的记录我们得知,因为一些低版本浏览器的实现存在 bug,在这些浏览器
下使用 report 函数进行数据上报会丢失 30%左右的数据,也就是说, report 函数并不是每一次
都成功发起了 HTTP 请求。丢失数据的原因是 img 是 report 函数中的局部变量,当 report 函数的
调用结束后, img 局部变量随即被销毁,而此时或许还没来得及发出 HTTP 请求,所以此次请求
就会丢失掉。
现在我们把 img 变量用闭包封闭起来,便能解决请求丢失的问题:
var report = (function(){
var imgs = [];
return function( src ){
var img = new Image();
imgs.push( img );
img.src = src;
}
})();
function f1(){
var n=999;
nAdd=function(){n+=1}
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
nAdd();
result(); // 1000
将代码封装成一个闭包形式,等待时机成熟的时候再使用
比如实现柯里化和反柯里化
常见错误
this 相关拓展
react中this的应用
这里的bind()
是es5中的api
原型链
继承相关
实现继承的方式
obj.create()来继承
function Person() {}
function Student() {}
Stduent.prototype = Person.prototype;//这种是错误的,这样改变Student的同时也改变了Person。比如student 会学习一个学科,但是并不是人也会,所以我们不能在改写student的时候把person也改掉
Student.prototype = new Person(); //我们new Person的时候得到了一个Person的实例,且这个实例是指向Person.prototype
// var o = new Person 则 o.__proto__ = Person.prototype
//上面这种调用了Person构造函数实现了继承。但是就是因为调用了构造函数
Student.prototype = Object.create(Person.prototype);//创建一个空对象,并且对象的原型指向Person.prototype 这样既保证可以继承Person.prototype上的方法,Student.prototype又有自己的空的对象 自己的修改不会影响原型链上的内容
//利用了原型链写 不向上查找的特性
Student.prototype.constructor = Student;
call()/ apply()继承
function Person() { this.x = 1; }
function Student() { Person.apply(this); this.y = 2; }
var s = new Student();
s.x;
s.y;
深拷贝浅拷贝
本质上:简单的来说就是,在有指针的情况下,浅拷贝只是增加了一个指针指向已经存在的内存,而深拷贝就是增加一个指针并且申请一个新的内存,使这个增加的指针指向这个新的内存,采用深拷贝的情况下,释放内存的时候就不会出现在浅拷贝时重复释放同一内存的错误!
js中深复制和浅复制只针对像 Object, Array 这样的复杂对象的。简单来说,浅复制只复制一层对象的属性,而深复制则递归复制了所有层级。
对象中的拷贝
深拷贝
What is the most efficient way to deep clone an object in JavaScript?
数组中的浅拷贝
下面是一个简单的浅复制实现:
var obj = { a:1, arr: [2,3] };
var shadowObj = shadowCopy(obj);
function shadowCopy(src) {
var dst = {};
for (var prop in src) {
if (src.hasOwnProperty(prop)) {
dst[prop] = src[prop];
}
}
return dst;
}
因为浅复制只会将对象的各个属性进行依次复制,并不会进行递归复制,而 JavaScript 存储对象都是存地址的,所以浅复制会导致 obj.arr 和 shadowObj.arr 指向同一块内存地址,大概的示意图如下。
导致的结果就是:
shadowObj.arr[1] = 5;
obj.arr[1] // = 5
而深复制则不同,它不仅将原对象的各个属性逐个复制出去,而且将原对象各个属性所包含的对象也依次采用深复制的方法递归复制到新对象上。这就不会存在上面 obj 和 shadowObj 的 arr 属性指向同一个对象的问题。
var obj = { a:1, arr: [1,2] };
var obj2 = deepCopy(obj);
结果如下面的示意图所示:
需要注意的是,如果对象比较大,层级也比较多,深复制会带来性能上的问题。在遇到需要采用深复制的场景时,可以考虑有没有其他替代的方案。在实际的应用场景中,也是浅复制更为常用。
更多可以看这几篇文章
http://www.cnblogs.com/racyil...
http://www.cnblogs.com/guorui...
http://jerryzou.com/posts/div...
OOP
这几篇文章是要看一看的
Javascript 面向对象编程(一):封装
Javascript面向对象编程(二):构造函数的继承
Javascript面向对象编程(三):非构造函数的继承
class
class可以看作是一个语法糖,让对象原型的写法更加清晰。
模拟(参数)重载
在java中我们可以通过参数的类型的区别或者数量的区别,让同样一个函数名可以根据不同的参数列表的情况来调用相应的函数。
但在javascript中由于是弱类型的,没有直接的机制实现参数的重载。javacript中函数参数类型是不确定的传入的参数个数也是任意的,可以通过判断实际传入的参数的个数来做模拟的重载
调用子类方法
我们在实现继承的时候经常要调用子类的方法
链式调用
这里可以看看 jQuery
抽象类
defineProperty(ES5)
线程
JS单线程指的是js主引擎单线程,但是浏览器是多线程的,浏览器里面可能包含UI渲染,HTTP请求,用户点击等线程。当多个事件触发的时候,JS会把异步事件依次的放入eventloop里等同步事件执行完之后,再去队列里一个个执行event
eventloop
callback
promise
async/await
https://segmentfault.com/a/11...
拾遗
e.target.value
JavaScript 常用方法总结
面试的信心来源于过硬的基础
参考
《JavaScript高级程序设计》
《你所不知道的JavaScript》
http://www.cnblogs.com/wangfu...
JavaScript深入浅出
js知识点思维导图
《js经典实例》
javascript 总结(那些剪不断理还乱的关系)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。