如今的前端框架层出不穷,3个月就要从新入门一次前端的现状,让咱们来不及学好基础就开始上手框架。经常就由于这样,咱们会很快到达基础
基础技术瓶颈
,基础是全部技术的核心,在跳槽季从新温故了一遍 javascript 基础,有收获,整理出来分享给你们。
javascript
中全部的变量均可以当作对象使用,除了undefined
和 null
,咱们测试下javascript
false.toString() // "false" [1,2,3].toString() //"1,2,3" 1..toString() //"1" ({a:'33'}).toString() //"[object Object]"
undefined.toString() //Uncaught TypeError null.toString() //Uncaught TypeError
数值和对象虽然能调用 toString
方法,可是在写法上须要注意下html
number
调用时不能直接数值后面直接调用toString
方法,由于 js
会将点运算符解析为数值的小数点前端
1.toString() //Uncaught SyntaxError 1..toString() //"1"
对象直接调用toString
方法时,须要用小括号包裹起来,否则js
会将对象的花括号识别成块,从而报错java
{a:'33'}.toString() // Uncaught SyntaxError ({a:'33'}).toString() // "[object Object]"
删除对象的属性惟一的方法是使用delete
操做符,设置元素属性为undefined
或则null
并不能真正删除,只是移除了属性和值的关联
var test = { name:'bbt', age:'18', love:'dog' } test.name = undefined test.age = null delete test.love for (var i in test){ console.log(i+':'+test[i]) }
运行结果es6
name:undefined age:null undefined
只有 love
被正则删除,name
和 age
仍是能被遍历到面试
在javascript
中,经过关键字new
调用的函数就被认为是构造函数,咱们能够经过构造函数建立对象实例
可是在使用过程当中你必定发现了,每实例化一个对象,都会在实例对象上创造构造函数的方法和属性。假若建立的实例比较多,重复建立同一个方法去开辟内存空间就会显得十分浪费,咱们能够经过把被常常复用的方法放在原型链上。编程
javascript
和一些咱们所了解的面向对象编程的语言不太同样,在es6
语法之前,咱们是经过原型链来实现方法和属性的继承
function Child(){ this.name = 'bbt' } Child.prototype = { title:'baba', method: function() {} }; function Grandson(){} //设置 Grandson 的 prototype 为 Child 的实例 Grandson.prototype = new Child() //为 Grandson 的原型添加添加属性 age Grandson.prototype.age = 40 // 修正 Grandson.prototype.constructor 为 Grandson 自己 Grandson.prototype.constructor = Grandson; var xiaomin = new Grandson() //原型链以下 xiaomin // Grandson的实例 Grandson.prototype // Child的实例 Grandson.prototype //{title:'baba',...} Object.prototype {toString: ... /* etc. */};
对象的属性查找,javascript
会在原型链上向上查找属性,直到查到 原型链顶部,因此,属性在原型链的越上端,查找的时间会越长,查找性能和复用属性方面须要开发者本身衡量下。数组
hasOwnProperty
方法可以判断一个对象是否包含自定义属性,而不是在原型链上的属性前端框架
var test = {hello:'123'} Object.prototype.name = 'bbt' test.name //'bbt' test.hasOwnProperty('hello') //true test.hasOwnProperty('name') //false
for in
循环能够遍历对象原型链上的全部属性,如此咱们将 hasOwnProperty
结合循环for in
可以获取到对象自定义属性微信
var test = {hello:'222'} Object.prototype.name = 'bbt' for(var i in test){ console.log(i) // 输出两个属性,hello ,name } for(var i in test){ if(test.hasOwnProperty(i)){ console.log(i)//只输出 hello } }
除了上面的方法,getOwnPropertyNames
和 Object.keys
方法,可以返回对象自身的全部属性名,也是接受一个对象做为参数,返回一个数组,包含了该对象自身的全部属性名。
var test = {hello:'222'} Object.prototype.name = 'bbt' Object.keys(test) //["hello"] Object.getOwnPropertyNames(test) //["hello"]
那 getOwnPropertyNames
和 Object.keys
的用法有什么区别呢
Object.keys
方法只返回可枚举的属性,Object.getOwnPropertyNames
方法还返回不可枚举的属性名。
var a = ['Hello', 'World']; Object.keys(a) // ["0", "1"] Object.getOwnPropertyNames(a) // ["0", "1", "length"] // length 是不可枚举属性
咱们一般会使用函数声明或函数赋值表达式来定义一个函数,函数声明和变量声明同样都存在提高的状况,函数能够在声明前调用,可是不能够在赋值前调用
函数声明
foo(); // 正常运行,由于foo在代码运行前已经被建立 function foo() {}
函数表达式
foo; // 'undefined' foo(); // 出错:TypeError var foo = function() {};
变量提高是在代码解析的时候进行的,foo() 方法调用的时候,已经在解析阶段将 foo 定义过了。赋值语句只在代码运行时才进行,因此在赋值前调用会报错
一种比较少用的函数赋值操做,将命名函数赋值给一个变量,此时的函数名只对函数内部可见
var test = function foo(){ console.log(foo) //正常输出 } console.log(foo) //Uncaught ReferenceError
在javascript
中 ,this
是一个比较难理解的点,不一样的调用环境会致使this
的不一样指向,可是惟一不变的是this
老是指向一个对象
简单的说,this
就是属性和方法当前所在的对象(函数执行坐在的做用域),平时使用的 this
的状况能够大体分为5种
调用方式 | 指向 |
---|---|
1. 全局范围调用 | 指向 window 全局对象 |
2. 函数调用 | 指向 window 全局变量 |
3. 对象的方法调用 | 指向方法调用的对象 |
4. 构造函数调用 | 指向构造函数建立的实例 |
5. 经过,call ,apply ,bind 显示的指定 this指向 | 和传参有关 |
Function.call
语法:function.call(thisArg, arg1, arg2, …),thisArg
表示但愿函数被调用的做用域,arg1, arg2, …
表示但愿被传入函数额参数 , 若是参数为空、null
和undefined
,则默认传入全局对象。
代码示例
var name = 'xiaomin' var test = {name : 'bbt'} function hello( _name ){ _name ?console.log(this.name,_name): console.log(this.name) } hello() //xiaomin hello.call(test) //bbt hello.call(test,'xiaohong') //bbt xiaohong hello.call() //xiaomin hello.call(null) //xiaomin hello.call(undefined) //xiaomin
Function.apply
语法和
call
方法相似,不一样的是,传入调用函数的参数变成以数组的形式传入,即 func.apply(thisArg, [argsArray])
改造上面的示例就是
hello.apply(test,['xiaomin'])
Function.bind
bind
方法用于将函数体内的this
绑定到某个对象,而后返回一个新函数。
var d = new Date(); d.getTime() var print = d.getTime; //赋值后 getTime 已经不指向 d 实例 print() // Uncaught TypeError
解决方法
var print = d.getTime.bind(d)
容易出错的地方
容易出错的地方,函数调用,this
老是指向 window
全局变量,因此在对象的方法里若是有函数的调用的话(闭包的状况),this
是会指向 全局对象的,不会指向调用的对象,具体示例以下
var name = 'xiaomin' var test = { name : 'bbt' } test.method = function(){ function hello(){ console.log(this.name) } hello() } // 调用 test.method() // 输出 xiaomin
若是须要将 this
指向调用的对象,能够将对象的 this
指向存储起来,一般咱们使用 that
变量来作这个存储。改进以后的代码
var name = 'xiaomin' var test = { name : 'bbt' } test.method = function(){ var that = this function hello(){ console.log(that.name) } hello() } // 调用 test.method() // 输出 bbt
闭包咱们能够理解成是在函数内部定义的函数
在 javascript
中,内部做用域能够访问到外部做用域的变量,可是外部做用域不能访问内部做用域,须要访问的时候,咱们须要经过建立闭包,来操做内部变量
function test(_count){ var count = _count return { inc:function(){ count++ }, get:function(){ return count } } } var a = test(4) a.get()//4 a.inc() a.get()//5
闭包中常会出错的面试题
for(var i = 0; i < 10; i++) { setTimeout(function() { console.log(i); }, 0); }
不少同窗会以为,上面的代码会正常输出0到9,可是实际是输出十次10。遇到这个题目,除了闭包的概念要理解清楚,你还须要知道,setTimeout
内的代码会被异步执行,代码会先执行全部的同步代码,即上面的这段代码会先将 for
循环执行,此时 i
的值为 10,console.log(i) 一直引用着全局变量的 i 因此会输出十次 10
改进代码,咱们在 for
循环里建立一个闭包,把循环自增的 i
做为参数传入
for(var i = 0; i < 10; i++) { (function(e) { setTimeout(function() { console.log(e); }, 1000); })(i); }
javascript
是异步的单线程运行语言,其余代码运行的时候可能会阻塞setTimeout
&&setInterval
的运行
console.log(1) setTimeout(function(){ console.log(2) }, 0); console.log(3) 输出结果: 1,3,2 //setTimeout 被阻塞
处理阻塞的方法是将setTimeout
和 setInterval
放在回调函数里执行
function test(){ setTimeout(function(){ console.log(2) }, 0); }
setTimeout
和 setInterval
被调用时会返回一个 ID 用来清除定时器
手工清除某个定时器
var id = setTimeout(foo, 1000); clearTimeout(id);
清楚全部的定时器
var lastId = setTimeout(function(){ console.log('11') }, 0); for(var i=0;i<lastId;i++;){ clearTimeout(i); }
获取最后一个定时器的id,遍历清除定时器,能够清除全部的定时器。
数值、字符串、布尔值——在必定条件下,也会自动转为对象,也就是原始类型的“包装对象”。
咱们能够经过构造函数,将原始类型转化为对应的对象即包装对象,从而是原始类型可以方便的调用某些方法
数值,字符串,布尔值的类型转换函数分别是 Number,String,Boolean
,在调用的时候在函数前面加上New 就变成了构造函数,可以蒋对应的原始类型转化为“包装对象”
var v1 = new Number(123); var v2 = new String('abc'); var v3 = new Boolean(true); typeof v1 // "object" typeof v2 // "object" typeof v3 // "object" v1 === 123 // false v2 === 'abc' // false v3 === true // false
类型转换分为强制类型转换和自动转换,javascript
是动态类型语言,在到吗解析运行时,须要的数据类型和传入的数据类型不一致的时候,javascript
会进行自动类型转化。固然,你也能够经过类型转换方法进行强制类型装换。
平常开发中,咱们最经常使用的数据类型自动转换不过就下面三种状况
不一样数据类型之间相互运算
'2'+4 // '24'
对非布尔值进行布尔运算
if('22'){ console.log('hello') }
对非数据类型使用一元运算符
+'12' //12
咱们也经过 Number ,String,Boolean
来进行强制数据类型转换。强制类型转化的规则有点复杂,咱们来了解一下。
Number 转换 引用阮老师的详细解释
第一步,调用对象自身的valueOf方法。若是返回原始类型的值,则直接对该值使用Number函数,再也不进行后续步骤。 第二步,若是 valueOf 方法返回的仍是对象,则改成调用对象自身的 toString 方法。若是 toString 方法返回原始类型的值,则对该值使用 Number 函数,再也不进行后续步骤。 第三步,若是 toString 方法返回的是对象,就报错。
String
转换方法一样也是经过调用原对象的 toString
方法和 valueOf
方法,可是不一样的是 String
函数会先调用 toString
方法进行转换
Boolean
的转换规则会相对简单一些,除了几个特殊的值,都会被转化为 true
undefined null +0或-0 NaN ''(空字符串)
可是要注意
Boolean('false') //true
typeof
操做符返回数据类型,可是因为javascript
设计的历史缘由,typeof
现已经不能知足咱们如今对于类型判断的要求了
Value | Class | Type |
---|---|---|
"foo" | String | string |
new String("foo") | String | object |
1.2 | Number | number |
new Number(1.2) | Number | object |
true | Boolean | boolean |
new Boolean(true) | Boolean | object |
new Date() | Date | object |
new Error() | Error | object |
[1,2,3] | Array | object |
new Array(1, 2, 3) | Array | object |
new Function("") | Function | functio |
/abc/g | RegExp | object (function in Nitro/V8) |
new RegExp("meow") | RegExp | object (function in Nitro/V8) |
{} | Object | object |
new Object() | Object | object |
null | null | object |
咱们能够看到,typeof
不能区分对象的数组和日期,还会把 null
判断成对象,那咱们通常是何时用 typeof
呢。咱们能够用来判断一个已经定义的变量是否被赋值。
var a if(typeof a == 'undefined'){ console.log('a 已经被定义') }
instanceof
操做符一般用来判断,一个对象是否在另外一个对象的原型链上,须要注意的是instanceof
的左值是对象,右值是构造函数
// defining constructors function C() {} function D() {} var o = new C(); // true, because: Object.getPrototypeOf(o) === C.prototype o instanceof C; // false, because D.prototype is nowhere in o's prototype chain o instanceof D;
#### Object.prototype.toString
那么咱们有没有能够用来区分变量数据类型的方法呢,有,
Object.prototype.toString
一些原始数据类型也有 toString
方法,可是一般他们的 toString
方法都是改造过的,不能进行 数据类型判断,因此咱们须要用 Object
原型链上的 toString
方法
var a = 1234 a.toString() // '1234' Object.prototype.toString.call(a) // "[object Number]"
不一样类型返回的结果以下:
1. 数值 [object Number] 2. 字符串 [object String] 3.布尔值 [object Boolean] 4.undefined [object undefined] 5.null [object Null] 6.数组 [object Array] 7.arguments [object Arguments] 8.函数 [object function] 9.Error [object Error] 10.Date [object Date] 11.RegExp [object RegExp] 12.其余对象 [object object]
那么咱们就可以经过 Object.prototype.toString
方法,封装一个能够判断变量数据类型的函数了
function type(obj) { return Object.prototype.toString.call(obj).slice(8, -1); } type(function(){}) //"Function"
此次咱们从对象、函数、类型三方面入手了解了
javascript
中容易被忽视或则说比较难理解的地方,我会继续将我在学习中积累的内容分享给你们,若是你们以为文章有须要改进或则有其余想要了解的内容的,欢迎私信,评论或则微信我,646321933