你不知道的 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) ; //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) 这个办法就好,万能的!

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持我们!

(0)

相关推荐

  • JS重要知识点小结

    讲解还是以示例代码搭配注释的形式,这里做个小目录: JS代码预解析原理(包括三个段落): 函数相关(包括 函数传参,带参数函数的调用方式,闭包): 面向对象(包括 对象创建.原型链,数据类型的检测,继承). JS代码预解析原理 复制代码 代码如下: /****************** JS代码预解析原理 ******************/ /* JS代码预解析.变量作用域.作用域链等 应该能作为学习JS语言的入门必备知识. 下边给出些简要解释和一些典型的代码段,若要了解更多,能从网上搜索

  • javaScript知识点总结(必看篇)

    1.javaScript的概念:是一种表述语言,也是一种基于对象(Object)和事件驱动(EventDriven)的,安全性好的脚本语言,运行在客户端,从而减轻服务器端的负担,总结如下: 1.javaScript主要用来向HTML页面找那个添加交互行为. 2.javaScript是一种脚本语言,语法和java类似. 3.javaScript一般用来编写客户端脚本. 4.javaScript是一种解释性语言,边执行边解释. 2.javaScript由核心语法ECMAScript.浏览器对象模型(

  • JavaScript基础知识学习笔记

    JavaScript简介 一.定义:JavaScript是脚本语言,需要宿主文件,它的宿主文件是html文件. JavaScript 是一种轻量级的编程语言. JavaScript 是可插入 HTML 页面的编程代码. JavaScript 插入 HTML 页面后,可由所有的现代浏览器执行. 二.用法: 在html中的位置有三块: 1)head里面: 2)body里面: 3)</html>之后,为了保险起见一般写在</html>之后.<script language=&quo

  • javascript学习笔记(一)基础知识

    基本概念 javascript是一门解释型的语言,浏览器充当解释器. js执行引擎并不是一行一行的执行,而是一段一段的分析执行. 延迟脚本 HTML4.0.1中定义了defer属性,它的用途是表明脚本在执行时不会影响页面的构造.也就是说,脚本会延迟到整个页面都解析完毕后再执行.因此,在<script>元素中设置defer属性,相当于告诉浏览器立即下载,但延迟执行.在XHTML文档中,要把defer属性设置为defer="defer" 异步脚本 html5为<scrip

  • JavaScript中字面量与函数的基本使用知识

    JavaScript 字面量 在编程语言中,一个字面量是一个常量,如 3.14. 数字(Number)字面量 可以是整数或者是小数,或者是科学计数(e). 3.14 1001 123e5 字符串(String)字面量 可以使用单引号或双引号 be written with double or single quotes: "John Doe" 'John Doe' 表达式字面量 用于计算: 5 + 6 5 * 10 数组(Array)字面量 定义一个数组: [40, 100, 1, 5

  • javascript真的不难-回顾一下基础知识

    前言 说Jquery之前,先来学习一下Javascript(以后简称为JS)语言中的基础知识问题,其时对于每种编程语言来说基础知识都是大同小异的,对变量,函数,条件语句块,循环语句块等等,而对于每种语言在写法上到是显得各有不同,如JS里在定义变量时,你要用var去声局部变量的声明,而对于弱类型的语言JS来说,你也可以不加var,但不加它会认为这个变量为全局变量,这是要注意的. 变量 在程序运行过程中,其值可以发生改变的(呵呵,10多年前书上的定义) var people ; //声明一个名为pe

  • javascript正则表达式基础知识入门

    正则表达式的好处到底在哪里呢,下面我们先进行个了解: 我们用js中处理字符串的方法,写出取出字符串中数字的函数: var str='dgh6a567sdo23ujaloo932'; function getNumber(obj){ var arr=[]; for (var i = 0; i < obj.length; i++) { if (obj.charAt(i)>='0'&&obj.charAt(i)<='9'){ arr.push(obj.charAt(i)); }

  • js基础知识(公有方法、私有方法、特权方法)

    本文涉及的主题虽然很基础,在许多人看来属于小伎俩,但在JavaScript基础知识中属于一个综合性的话题.这里会涉及到对象属性的封装.原型.构造函数.闭包以及立即执行表达式等知识. 公有方法 公有方法就是能被外部访问并调用的方法. // 在对象中 var Restaurant = { name: 'McDonald', // 公有方法 getName: function() { return this.name; } } // 在构造函数中 function Person(name, age)

  • JavaScript知识点整理

    JavaScript一种直译式脚本语言,是一种动态类型.弱类型.基于原型的语言,内置支持类型.它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在HTML(标准通用标记语言下的一个应用)网页上使用,用来给HTML网页增加动态功能. 首先,还是用比较官方的文字描述来解释下JavaScript: JavaScript一种直译式脚本语言,是一种动态类型.弱类型.基于原型的语言,内置支持类型. 它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于

  • 每天一篇javascript学习小结(基础知识)

    1.字符转换 var s1 = "01"; var s2 = "1.1"; var s3 = "z";//字母'z'无法转换为数字,所以或返回NaN var b = false; var f = 1.1; var o = { valueOf: function() { return -1; } }; s1 = -s1; //value becomes numeric -1 s2 = -s2; //value becomes numeric -1.

随机推荐