你不知道的 javascript【推荐】


Posted in Javascript onJanuary 08, 2017

一、对象

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) ; //4
  }
 }
}
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()) ; // 3

作用域

作用域空间那个值变量和参数的可见性和生命周期,对程序员来说很重要,因为它减少了命名冲突,并且提供了自动内存管理。

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() // 2

当调用 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(); //3
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 相关文章推荐
jQuery cdn使用介绍
May 08 Javascript
js限制文本框只能输入数字方法小结
Jun 16 Javascript
Javascript 读取操作Sql中的Xml字段
Oct 09 Javascript
JS实现页面跳转参数不丢失的方法
Nov 28 Javascript
js多个物体运动功能实例分析
Dec 20 Javascript
js使用Replace结合正则替换重复出现的字符串功能示例
Dec 27 Javascript
Vue中的作用域CSS和CSS模块的区别
Oct 09 Javascript
vue webpack打包后图片路径错误的完美解决方法
Dec 07 Javascript
ES6入门教程之let、const的使用方法
Apr 13 Javascript
vue请求数据的三种方式
Mar 04 Javascript
微信小程序用canvas画图并分享
Mar 09 Javascript
JavaScript 数组去重详解
Sep 15 Javascript
js时间控件只显示年月
Jan 08 #Javascript
javascript函数的四种调用模式
Jan 08 #Javascript
jquery与ajax获取特殊字符实例详解
Jan 08 #Javascript
浅谈DOM的操作以及性能优化问题-重绘重排
Jan 08 #Javascript
完美解决node.js中使用https请求报CERT_UNTRUSTED的问题
Jan 08 #Javascript
jQuery+ajax的资源回收处理机制分析
Jan 07 #Javascript
JS实现iframe自适应高度的方法示例
Jan 07 #Javascript
You might like
很温暖很温暖的Lester Young
2021/03/03 冲泡冲煮
php报表之jpgraph柱状图实例代码
2011/08/22 PHP
linux实现php定时执行cron任务详解
2013/12/24 PHP
推荐一款MAC OS X 下php集成开发环境mamp
2014/11/08 PHP
php删除数组中重复元素的方法
2015/12/22 PHP
php源码的使用方法讲解
2019/09/26 PHP
laravel 去掉index.php伪静态的操作方法
2019/10/12 PHP
JS中setInterval、setTimeout不能传递带参数的函数的解决方案
2013/04/28 Javascript
JQuery处理json与ajax返回JSON实例代码
2014/01/03 Javascript
用jquery.sortElements实现table排序
2014/05/04 Javascript
JavaScript获取table中某一列的值的方法
2014/05/06 Javascript
详解JavaScript中Date.UTC()方法的使用
2015/06/12 Javascript
jquery实现拖动效果
2016/08/10 Javascript
javascript 动态样式添加的简单实现
2016/10/11 Javascript
利用jQuery实现滑动开关按钮效果(附demo源码下载)
2017/02/07 Javascript
整理关于Bootstrap列表组的慕课笔记
2017/03/29 Javascript
Vue-router结合transition实现app前进后退动画切换效果的实例
2017/10/11 Javascript
JS实现遍历不规则多维数组的方法
2018/03/21 Javascript
[59:30]VG vs LGD 2019国际邀请赛淘汰赛 胜者组 BO3 第二场 8.22
2019/09/05 DOTA
Python与shell的3种交互方式介绍
2015/04/11 Python
对numpy中数组元素的统一赋值实例
2018/04/04 Python
pyspark.sql.DataFrame与pandas.DataFrame之间的相互转换实例
2018/08/02 Python
PyQt5重写QComboBox的鼠标点击事件方法
2019/06/25 Python
opencv3/python 鼠标响应操作详解
2019/12/11 Python
CSS3中文字镂空、透明值、阴影效果设置示例小结
2016/03/07 HTML / CSS
CSS3中各种颜色属性的使用教程
2016/05/17 HTML / CSS
法国太阳镜店:Sunglasses Shop
2016/08/27 全球购物
美国第一大药店连锁机构:Walgreens(沃尔格林)
2019/10/10 全球购物
某同学的自我鉴定范文
2013/12/26 职场文书
建筑专业毕业生自荐信
2014/05/25 职场文书
会计岗位说明书
2014/07/29 职场文书
趣味运动会开幕词
2015/01/28 职场文书
销售经理岗位职责
2015/01/31 职场文书
2016读书月活动心得体会
2016/01/14 职场文书
Python 线程池模块之多线程操作代码
2021/05/20 Python
Python爬虫 简单介绍一下Xpath及使用
2022/04/26 Python