你不知道的javascript
一、对象
JavaScript简单类型有数字、字符串、布尔值、null、undefined,其他所有的值都是对象(数组、函数、正则表达式都是对象)。
数字、字符串、布尔值虽然拥有方法(包装对象),但并不是对象。
包装对象:
每当读取一个基本类型值的时候,后台会创建一个对象的基本包装类型的对象,从而能够调用一些方法来操作这些数据。
var s1 = 'abcdefg' ;
var s2 = s1.substring(2) ;
后台自动完成下列处理:
- 创建String类型的一个实例
- 在实例上调用指定的方法
- 销毁这个实例
所以上面代码等同于:
对象字面量
var flight = {
airline: "Oceanic",
number: 815,
departure: {
IATAL: "SYD",
time: "2004-09-22 14:55",
city: "Sydney"
},
arrival: {
IATA: "LAX",
time: "2004-09-23 10:42",
city: "Los Angeles"
}
}
检索
[]
:flight['number']
.
:flight.number
更新
通过赋值语句更新,如果属性名已经存在于对象中,则被替换;如果对象中没有那个属性名,则添加。
stooge['first-name'] = 'Jerome'
引用
对象赋值通过引用来传递,它们永远不会被拷贝。
var a = {
name: 'a'
}
var b = a
b.name = 'b'
console.log(a.name) // b
这里牵扯出 JavaScript 深拷贝和浅拷贝的问题
上例是浅拷贝使用Object.create可以进行深拷贝
var a = {
name: 'a'
}
var b = Object.create(a)
b.name = 'b'
console.log(a.name) // a
自定义方法深拷贝见下:
var deepCopy= function(source) {
var result={};
for (var key in source) {
result[key] = typeof source[key]===’object’? deepCoyp(source[key]): source[key];
}
return result;
}
此时 var b = deepCopy(a)
得到的 b
就和 a
没有引用关系,即修改 b
不会影响 a
了
原型
每个对象都连接到一个原型对象,并且从中继承属性。所有通过对象字面量创建的对象都连接到 Object.prototype
这个JavaScript中标准的对象。
创建一个对象时,可以选择某个对象作为它的原型:
var o = {o1:1,o2:function(){alert(1)}}
function F(){}
F.prototype = o
var f = new F()
反射
使用 hasOwnProperty
检查属性是否是对象独有的,它并不会检查原型链。
flight.hasOwnProperty('number'); //true
枚举
for in
可以遍历对象中所有的属性名(见深拷贝部分)
删除
delete
可以删除对象属性,不会触及原型链中的任何对象
减少全局变量污染
最小化使用全局变量的一个方法是创建唯一一个全局变量:
var App = {}
App.stooge = {
"first-name": "Joe",
"last-name": "Howard"
}
App.flight = {
airline: "Oceanic",
number: 815
}
减少全局变量污染另一个办法是使用闭包进行信息隐藏
二、函数
函数包含一组语句,是Javascript的基础模块单元,用于代码复用、信息隐藏和组合调用。
一般来说,所谓编程就是将一组需求分解成一组函数与数据结构的技能。
函数对象
函数就是对象,对象是键值对的集合并且拥有一个连到原型对象的隐藏连接。对象字面量产生的对象连接到 Object.prototype
,函数对象连接到 Function.prototype
(该原型对象本身又连接到 Object.prototype)。
var add = function(a,b){
return a + b;
}
函数表达式包含四部分:
- 保留字
function
- 函数名,可以被省略(匿名函数)
- 参数,逗号分隔
- 花括号中的语句
函数表达式允许出现在任何允许表达式出现的地方,函数也可以被定义在其他函数中。一个内部函数可以访问自己的参数和变量,同时它也能方便地访问它被嵌套在其中的那个函数的参数和变量。通过函数表达式创建的函数对象包含一个连到外部上下文的连接,被称为闭包。
调用
函数在调用的时候有两个附加参数:this
、arguments
。
this
是调用上下文,值取决于函数调用的模式。
1.方法调用模式
一个函数被保存为对象的一个属性时,即为一个方法。当一个方法被调用时,this
被绑定到该对象。
每个函数在创建时附有两个附加的隐藏属性:
- 函数上下文
- 实现函数行为的代码
每个函数对象在创建时也会带一个 prototype
属性,它的值是一个拥有 constructor
属性且值为该函数的对象。
函数表达式
函数对象可以通过函数表达式来创建:
var dog = {
name : 'xxx' ,
leg:{
sum : 4 ,
move:function(){
console.log(this) ; //Object,是leg对象,而不是dog对象,下面证明了
alert(this.name) ; //underfined
alert(this.sum) ; //
}
}
}
dog.leg.move();
2.函数调用模式
函数仅仅当做函数来调用时,this
被绑定到全局对象。
var a = 111 ;
function t1(){
var a = 1
function t2(){
console.log(this.a) //111,这其实很不合理,应该指向t2的。
}
t2()
}
t1()
这其实是语言设计上的一个错误,倘若语言设计正确,当内部函数被调用时,this
应该仍然绑定到外部函数的 this
变量。
3.构造器调用模式
如果一个函数前面带上 new 调用,那么将创建一个隐藏连接到该函数的 prototype
成员的新对象,同时 this 将被绑定到那个新对象上。
function Dog(name){
this.name = name ;
}
Dog.prototype.cry = function(){
alert(this.name)
}
var dog1 = new Dog('xxx');
dog1.cry(); // 'xxx'
4.Apply/Call调用模式
apply 接受两个参数,第一个是将被绑定给this的值,第二个就是一个参数数组。
call 与 apply 相同,不过第二个参数不是数组。
var dog = {
leg : 4 ,
color:'yellow'
}
var color = 'red' ;
function t(){
alert(this.color) ;
}
t(); // red , 因为指向this在函数中调用指向window
t.call(dog); //yellow , 把t()的作用域指向了dog
再来说说 arguments
,它是一个类数组对象(拥有length属性,但缺少所有数组方法)。通过它可以访问函数调用时传递给函数的参数列表。
返回
一个函数调用时,将暂停当前函数的执行,传递控制权和参数给新函数。它从第一个语句开始执行,并在遇到关闭函数体的 }
时结束。使得函数把控制权交还给调用该函数的程序部分。
return
语句可用来使函数提前返回,当 return 执行时,函数立即返回而不再执行余下的语句。一个函数总是会返回一个值,如果没有指定返回值,则返回 undefined
。
如果函数前加上 new
来调用,且返回值不是一个对象,则返回this(该新对象)。
异常
抛出异常
function add(a,b){
if(typeof a!=='number' || typeof b!=='number'){
throw {
name: 'TypeError',
message: 'add needs numbers'
}
}
return a + b;
}
throw
语句中断函数的执行,抛出一个 exception
对象,该对象包含可识别异常类型的 name
属性和一个描述性的 message
属性。
该 exception
对象将被传递到一个 try
语句的 catch
从句
try{
add('seven')
}catch(e){
console.log(e.name)
console.log(e.message)
}
如果在 try
代码块中抛出异常,控制权就跳转到其 catch
从句。
给类型增加方法
Number.prototype.integer = function(){
return Math[this < 0 ? 'ceiling' : 'floor'](this) //this指向实例
} var num = 10/3
console.log(num.integer()) ; //
作用域
作用域空间那个值变量和参数的可见性和生命周期,对程序员来说很重要,因为它减少了命名冲突,并且提供了自动内存管理。
JavaScript没有块级作用域,却有函数作用域:定义在函数中的参数和变量在函数外部是不可见的,而且在一个函数中的任何位置定义的变量在该函数中任何地方都可见。
下面这个例子与以对象字面量初始化对象不同,通过调用一个函数形式去初始化对象,返回一个对象字面量。此函数定义了一个 val
变量,该变量对 addVal
和 getVal
总是可用的,但函数的作用域使得其对其他程序来说是不可见的。
// 从设计模式的角度来说这是模块模式
var o = (function(){
var val = 0;
return {
addVal: function(){
val += 1
},
getVal: function(){
console.log(val)
}
}
})()
联想到之前我做的一个小游戏,是20秒内完成任务,使用 restTime
做倒计时变量。后来同事把restTime修改了,成绩贼高。最后我就是用这种办法把 restTime
像 val
一样隐藏了起来。
闭包
作用域的好处是内部函数可以访问定义它们的外部函数的参数和变量(除了this和arguments)。
var o = function(){
var val = 0;
return {
addVal: function(){
val += 1
},
getVal: function(){
console.log(val)
}
}
}
var oo = o()
oo.addVal()
oo.addVal()
oo.getVal() //
当调用 o
时,返回一个包含addVal
和getVal
的新对象,该对象的引用保存在 oo
中。虽然 o
返回了,但是 oo
的addVal
和getVal
有访问 val
的特权,它们可以访问被创建时所处的上下文,这就是闭包。
模块
可以使用函数和闭包来构造模块。模块是一个提供接口却隐藏状态与实现的函数或对象。
具体见『作用域』部分-模块模式
三、继承
当一个函数对象被创建时,Function 构造器产生的函数对象会运行类似这样的代码:
this.prototype = {constructor: this}
每个函数都会得到一个 prototype
对象,其值是包含一个 constructor
属性且属性值为该新函数对象。该 prototype
对象是存放继承特征的地方。
四、数组
区分数组和对象
一个常见的错误是在须使用数组时使用了对象,在须使用对象时使用了数组。其实规则很简单:当属性名是小而连续的整数时,用数组,否则就用对象。
JavaScript中数组 typeof
返回是 object
,这并不能区分数组和对象。
var is_array = function(value){
return value &&
typeof value === 'object' &&
typeof value.length === 'number' &&
typeof value.splice === 'function' &&
!(value.propertyIsEnumerable('length'))
}
- 首先,我们判断这个值是否为真,我们不接受
null
和其他为假的值。 - 其次,我们判断这个值的
typeof
运算结果是否是object
。对于对象、数组和null
来说,将得到true
。 - 第三,我们判断这个值是否有一个值为数字的
length
属性,对于数组是true
,对于对象则为false
- 第四,判断这个值是否包含一个
splice
方法。对于数组,返回true
- 最后,我们判断
length
属性是否是可枚举的
这真的很复杂,实际上,我一直是这样用的,什么类型都能检测,堪称万能:
var toString = Object.prototype.toString
function isObject(obj) {
return toString.call(obj) === "[object Object]"
}
function isString(obj) {
return toString.call(obj) === "[object String]"
}
function isArray(obj) {
return toString.call(obj) === "[object Array]"
}
function isFunction(obj) {
return toString.call(obj) === "[object Function]"
}
五、方法
Array
concat(item...)
返回一个新数组,并不会修改原数组
var a1 = [2,3]
var a2 = [332,12]
console.log( a1.concat(a2) ); //[ 2, 3, 332, 12 ]
join(separator)
把一个 array 构造成一个字符串,并用 separator
作为分隔符把它们连接在一起。
pop(item...)
移除数组中最后一个元素并返回该元素
push(item...)
将一个或多个元素添加到数组尾部,会修改原数组
reverse()
反转数组中元素的顺序,会修改原数组,返回当前数组
shift()
移除数组中第一个元素
slice(start,end)
从start开始,到end为止(不包括end,可选,默认值是length)复制数组
sort(comparefn)
对数组中的内容排序,并不能给数字排序,因为默认比较函数是假定要被排序的元素都是字符串。
比较函数接受两个参数,并且如果两个参数相等返回0,如果第一个参数应该排在前面,则返回一个负数,如果第二个参数应该排在前面,则返回一个正数。
// a 比 b小时返回 -1,而且根据上述规则 a 会排在前面
// 所以这是从小到大的排序 var arr = [1,123,341,34,123]
arr.sort(function(a,b){
if(a==b){
return 0
}else{
return a < b ? -1 : 1
}
})
splice(start,deleteCount,item...)
splice 从数组中移除一个或多个元素,并用新的item代替他们。
start是从数组中移除元素的开始位置,deleteCount是要移除的个数。
会修改原数组,返回一个包含被移除元素的数组。
var arr = [23,3,23,2]
var b = arr.splice(0,1) console.log(arr) ; //[ 3, 23, 2 ]
console.log(b) ; //[ 23 ]
deleteCount 为0时,则为添加新元素:
var arr = [23,3,23,2]
var b = arr.splice(1,0,'aa') console.log(arr) // [ 23, 'aa', 3, 23, 2 ]
console.log(b) // []
deleteCount 与 item的个数相等时,则为替换:
var arr = [23,3,23,2]
var b = arr.splice(1,1,'aa') console.log(arr) //[ 23, 'aa', 23, 2 ]
console.log(b) //[ 3 ]
unshift(item...)
将item从数组头部插入数组
Function
apply(thisArg,argArray)
见 『Apply/Call调用模式』
Number
toFixed(fractionDigits)
把这个 number 转换成一个十进制形式的字符串。可选参数 fractionDigits
控制其小数点后的数字位数。
Math.PI.toFixed(); //
Math.PI.toFixed(2); //3.14
Math.PI.toFixed(4); //3.1415
toPrecision(precision)
同 toFixed
,参数控制有效数字的位数
toString()
将number转换成字符串
Object
hasOwnProperty(name)
只检查此对象中的属性,原型链中得同名属性不会被检查。如果存在此属性则返回 true
。
String
charAt(pos)
返回在字符串中pos处的字符
charCodeAt(pos)
返回不是一个字符串,而是以整数形式表示的字符码位
concat(string...)
与其他字符串连接起来构造一个新字符串,不常用,因为 +
也能满足需求
indexOf(searchString,pos)
在字符串内查找另一个字符串 searchString,如果被找到,则返回第一个匹配字符的位置,否则返回 -1 。
可选参数 pos 设置从字符串的某个指定位置开始查找。
lastIndexOf(searchString,pos)
与indexOf类似,不同从末尾开始查找
slice(start,end)
复制字符串的一部分构造一个新的字符串
split(separator,limit)
把字符串分割成片段创建数组,limit可限制被分割的片段数量。
一个有意思的技巧:
new Array(11).join('0').split('') //生成10个元素为0的数组
toLowerCase()
将字符串中所有字母转化为小写
toUpperCase()
将字符串中所有字母转化为大写
六、糟粕
这一部分用来吐槽JS这门语言设计上不周到的地方
全局变量
共三种方法定义全局变量:
- 脱离任何函数var语句
var foo = value
- 直接添加一个属性到全局对象中,全局对象是所有全局变量的容器。在web中,全局对象是
window
:window.foo = value
- 使用未声明的变量,这被称为隐式的全局变量:
foo = value
之前说过,可以通过 创建一个全局变量 和 闭包 减少全局变量污染(注意,只是减少,没办法避免,总要有暴露出来的变量,不要钻牛角尖)。
作用域
没有块级作用域,只有函数作用域
自动插入分号
JavaScript 有一个机制,会试图通过自动插入分号来修正有缺损的程序。它有可能会掩盖更为严重的错误。
return
{
status: true
}
看起来是返回一个对象,但是自动插入分号让它返回了undefined
,这样可以避免:
return {
status: true
}
typeof
typeof并不能正确地检测数据类型:
typeof null ; //object
所以使用 Object.prototype.toString.call(null)
这个办法就好,万能的!
你不知道的javascript的更多相关文章
- 《你不知道的JavaScript》整理(二)——this
最近在读一本进阶的JavaScript的书<你不知道的JavaScript(上卷)>,这次研究了一下“this”. 当一个函数被调用时,会创建一个活动记录(执行上下文). 这个记录会包含函 ...
- 《你不知道的JavaScript》整理(一)——作用域、提升与闭包
最近在读一本进阶的JavaScript的书<你不知道的JavaScript(上卷)>,里面分析了很多基础性的概念. 可以更全面深入的理解JavaScript深层面的知识点. 一.函数作用域 ...
- 你不知道的Javascript(上卷)读书笔记之一 ---- 作用域
你不知道的Javascript(上卷)这本书在我看来是一本还不错的书籍,这本书用比较简洁的语言来描述Js的那些"坑",在这里写一些博客记录一下笔记以便消化吸收. 1 编译原理 在此 ...
- 你不知道的JavaScript上卷笔记
你不知道的JavaScript上卷笔记 前言 You don't know JavaScript是github上一个系列文章 初看到这一标题的时候,感觉怎么老外也搞标题党,用这种冲突性比较强的题目 ...
- 【读书笔记】-- 你不知道的JavaScript
<你不知道的JavaScript>是一个不错的JavaScript系列书,书名可能有些标题党的意思,但实符其名,很多地方会让你有耳目一新的感觉. 1.typeof null === &qu ...
- 读书笔记-你不知道的JavaScript(上)
本文首发在我的个人博客:http://muyunyun.cn/ <你不知道的JavaScript>系列丛书给出了很多颠覆以往对JavaScript认知的点, 读完上卷,受益匪浅,于是对其精 ...
- 读《你不知道的JavaScript(上卷)》后感-作用域闭包(二)
github原文 一. 序言 最近我在读一本书:<你不知道的JavaScript>,这书分为上中卷,内容非常丰富,认真细读,能学到非常多JavaScript的知识点,希望广大的前端同胞们, ...
- 你不知道的javaScript上卷(第一章 作用域是什么)
在写这篇博客时这本书我已经是看过一遍了,为了加深印象和深入学习于是打算做这系列的前端经典书籍导读博文,大家如果觉得这本书讲的好可以自己买来看看,我是比较喜欢看纸质版书的,因为这样才有读书的那种感觉. ...
- JavaScript中的this(你不知道的JavaScript)
JavaScript中的this,刚接触JavaScript时大家都在大肆渲染说其多么多么的灵巧重要,然而自己并不关心:随着自己对JavaScript一步步深入了解,突然恍然大悟,原来它真的很重要!所 ...
- 你不知道的Javascript:有趣的setTimeout
你不知道的Javascript:有趣的setTimeout 有时候,小小的细节往往隐藏着大大的智慧今天在回顾JavaScript进阶用法的时候,发现一个有趣的问题,话不多说,先上代码: for(var ...
随机推荐
- bzoj 4300: 绝世好题
4300: 绝世好题 Time Limit: 1 Sec Memory Limit: 128 MB Description 给定一个长度为n的数列ai,求ai的子序列bi的最长长度,满足bi& ...
- 删除配置文件解决OS X各种WiFi无法连接的顽固问题,解决MAC无法连接wif的情况 Preferences
删除配置文件解决OS X各种WiFi无法连接的顽固问题 删除配置文件解决OS X各种WiFi无法连接的顽固问题1 记住现在wifi的密码并将wifi关闭2 前往文件夹/Library/Preferen ...
- ios取证
摘录自:<IOS取证实战> Andrew Hoog著 viaForensics公司,今年已经募集到1600万创业基金 2007-2011年初,AT&T是在美国唯一为iphone提供 ...
- mycat初探
1:安装客户端 yum install mysql 2:安装服务端 yum install mysql-server 3:mycat要求不区分大小写 my.cnf(/etc/my.cnf)的[mysq ...
- dubbo发布web服务实例
dubbo角色与调用执行过程 dubbo节点角色说明:provider: 暴露服务的服务提供方consumer: 调用远程服务的服务消费方registry: 服务注册于发现的注册中心monitor: ...
- ios开发中的Swift面向对象
iOS在现代计算机语言中,面向对象是非常重要的特性,Swift语言也提供了面向对象的支持.而且在Swift语言中,不仅类具有面向对象特性,结构体和枚举也都具有面向对象特性. 1.Swift中的类和结构 ...
- 【转】Log4net用法
Log4net用法 http://www.cnblogs.com/hfliyi/archive/2012/05/20/2510783.html
- 正则指引-量词demo
class Program { static void Main(string[] args) { string str = "1\"3"; var re1 = Rege ...
- 利用 a 标签自动解析 url
很多时候,我们有从 url 中提取域名,查询关键字,变量参数值等的需求,然而我们可以让浏览器方便地帮助我们完成这一任务而不用写正则去抓取.方法就是先创建一个 a 标签然后将需要解析的 url 赋值给 ...
- Https 公钥、私钥、证书
.https的握手协议: http://blog.csdn.net/clh604/article/details/221799072.证书的概念:http://blog.csdn.net/sealya ...