JavaScript学习(3) - 聊聊原型链- 1. 变量

619 阅读12分钟

《JavaScript高级程序设计(第三版)》学习笔记

相关内容:

JavaScript学习(3)- 聊聊原型链- 1. 变量

JavaScript学习(3)- 聊聊原型链- 2. 对象与原型

JavaScript学习(3)- 聊聊原型链- 3. 原型链与继承


关键词:JavaScript变量、基本类型、引用类型、instanceof、typeof、Object、Array、function

学习思路:学习原型链,需要先了解变量的知识,然后学习对象的创建、对象的原型,最后才是继承的关系

1. 变量的基本概念

基本类型引用类型
概念简单数据段可能由多个值构成的对象
数据类型Undefined, Null, Boolean, Number, StringObject
赋值var name = 13var name = new Object()
name.age = 13
复制引用Var num2 = num1
num1和num2相互独立,改变num1不影响num2
var obj2 = obj1
改变obj1会影响obj2,实际引用的相同对象

2. 基本类型

数据类型英文表达式typeOf 返回结果
未定义undefined"undefined"
布尔值Boolean"boolean"
字符串String"string"
数值Number"number"
NullNull"object"
函数function"function"

3. 引用类型

常见的引用类型:Object, Array, Date, RegExp, Function

也可以通过自定义构造函数创建自定义的引用类型,但是自定义类型本质上都是继承于默认的引用类型

3.1 检测引用类型 - instanceof

使用typeof并不能检测出具体是什么引用类型(object, array, date.....)

例如:null和object的typeof值都是object,无法区分

  1. 判断具体类型 - instanceof - 返回true/false
// instanceof样例
var o = new Object()

alert(o instanceof Array)
alert(o instanceof Constructor)
alert(o instanceof RegExp)  
  1. 对于某些类型,同样可用自身的类型检测方法
// Array类型 - 检测方法:Array.isArray()
alert(Array.isArray(colors)) // true

3.2 Object类型

// 创建Object
// 1. new一个Object对象,使用Object构造函数,并添加属性
var person = new Object()
person.name = "Cindy"
person.age = 27

// 2. 对象字面量法 - 用JSON对象的方式直接创建
var person = {
  name: "Cindy",
  age: 27
}
// 备注:对象字面量法不设置属性值创建时,同 new Object() 相同
var person = {} // 和 new Object() 相同

// 对象访问方法
person.name     // 输出:Cindy
person["name"]  // 输出:Cindy - 方括号传入string的方法便于传入变量,以动态获取对象中不同值

3.3 Array类型

// 创建方法
// 1. new一个Array对象,使用Array构造函数
var colors = new Array()
var colors = new Array(20)
var colors = new Array('red', 'blue', 'yellow')
var colors = Array("Gray") // 可以不用new

// 2. 数组字面量法
var colors = []
var colors = ['red', 'blue', 'yellow']


// 对象访问方法
// 1. Array对象包含length属性
alert(colors.length) // 3

// 2. Array对象可用下角标引用
colors[0] = "gray"
alert(colors[0]) // 输出:“gray”

3.4 Date类型

  • 推荐momentjs库进行时间日期转换
// 创建一个日期对象,自动获取当前日期
var now = new Date()

// 接收日期表示字符串,转换为相应日期毫秒数
Date.parse("6/13/2008")

// 日期格式化
toDateString()       // 以特定实现的格式显示星期几、月、日和年
toTimeString()       // 以特定实现的格式显示时、分、秒和时区
toLocaleDateString() // 以特定于地区的格式显示星期几、月、日和年
toLocaleTimeString() // 以特定于地区的格式显示时、分、秒
toUTCString()        // 以特定实现的格式显示UTC日期

3.5 RegExp类型

使用类似Perl语法,创建正则表达式

var expression = / pattern / flag

flag - 表名正则表达式行为:

  1. g - 全局模式,应用于所有字符串
  2. i - 不区分大小写
  3. m - 多行模式,到达一行文本末尾时还会继续查找下一行中是否存在与模式匹配的项

3.6 Function类型

3.6.1 函数声明方法

解析器会率先读取函数声明,添加到执行环境中,故调用时,代码可写在函数声明之前

// 函数声明 - 语法:
function funName (arg0, arg1, arg2) {
  // 函数体
}

// 函数声明提升 - 调用语句可以在函数声明之前,因为执行前代码会先读取函数声明
sayHi()
function sayHi() {
  alert('Hi')
}

3.6.2 函数表达式法

解析器无法对函数声明进行提升,率先添加到执行环境,故调用时,必须写在表达式之后

// 函数表达式 - 语法 - 将一个匿名函数赋值给一个变量
// 匿名函数:function没有命名
var funName = function (arg0, arg1, arg2) {
  // 函数体
}

// 函数表达式不能进行函数声明提升,调用要在表达式后面
var sayHi = function () { 
  alert('Hi')
}
sayHi()

3.6.3 关于function需要注意的问题:

  1. function指针引用
// 指针引用
var sum2 = sum1 // sum1和sum2均指向同一个function内容
  1. function没有重载
// 用指针解释function没有重载
var add1 = function (num) { return num + 100 }
add1 = function(num) { return num + 200 } //创建第二个函数的时候,实际上覆盖了第一个引用,故没有重载

3.6.4 function的传参 - arguments

3.6.4.1 arguments理解

  1. function中传入参数可不必非要声明,可以使用arguments对象来获取,不显式地使用命名参数
function sayHi() {
  alert("Hello " + arguments[0] + ',' + arguments[1])
}
  1. arguments对象类似Array的调用方式,但并非Array的实例
  2. function中传入的参数数量可不必与声明的参数个数一致,因为在function内部都是以数组形式获取参数值的
function sayHi (name1, name2) {
  alert("Hello " + name1 + ',' + name2)
}

sayHi('Alice')
sayHi('Alice', 'Bob')
sayHi('Alice', 'Bob', 'Clair')

// 注意:arguments[0]和name1是对应的,值是一样的
  1. arguments也具有length属性,以获得参数的个数
function argLength() {
  alert(arguments.length)
}

argLength('arg1', 'arg2')

3.6.4.2 基本类型传参

// function中的num和传入的参数num指向的是同一个argument对象 - 局部变量
function add (num) {
  num += 10
  return num
}
  
var count = 20
var result = add(count)
alert(count) // 20 - 原数值不产生变化
alert(result) // 30 - 局部变量结果产生变化 - 故function传参为按值传递

3.6.4.3 引用类型传参

function中对象的传参,本质上也是按值传递,但由于对象本身的赋值是引用赋值,所以arguments和传入的引用参数指向的是同一个object

// function中的argument和传入的person指向了内存中同一个object,
// 所以person.name会产生变化,但并不代表function传参中局部变量会影响全局变量
function setName(obj) {
  obj.name="Elanie"
  obj = new object()
  obj.name = "Henry" // 仅为内部重写的obj会赋值为Henry,而不会影响外部传入的person.name,重写的obj在函数结束时自动销毁
}

var person = new Object()
setName(person)
alert(person.name) // "Elanie"

3.6.4.4 补充:可将function作为参数传入

因为function本身就是变量,所以function也可以作为参数进行传递

// 1. 将function作为参数传递到另一个function中
function add1(num) {
  return num + 1
}

function add2(num2) {
  return num2 + 2
}

var result = add2(add1(10)) // 13

// 2. 从函数中返回一个函数
function createCompareFunction(propName) {
  return function (obj1, obj2) {
    var value1 = obj1[propName]
    var value2 = obj2[propName]
    if (value1 < value2) return -1
    else if (value1 > value2) return 1
    else return 0
  }
}

var data = [
  { name: 'name1', age: 20 },
  { name: 'name2', age: 28 }
]
data.sort(createCompareFunction('age'))

3.6.5 函数内部属性 - this、arguments

3.6.5.1 arguments.callee()

备注:严格模式中禁止使用callee

// 以阶乘函数作为样例进行说明:
// 1. 常规方法:必须调用名称同函数名称一致
function fac (num) {
  if (num <= 1) return 1
  else return num * fac(num - 1) // 存在问题:一旦后面对fac变量进行重新赋值/重写,则会导致本函数调用失败      
}

// 2. 解决方法:用arguments.callee()方法消除紧密耦合问题,使无论调用函数时写什么名字都能正常完成递归调用
function fac (num) {
  if (num <= 1) return 1
  else return num * arguments.callee(num - 1) // 消除紧密耦合,通过arguments获取方法
}

// 测试:对上述使用arguments.callee()方法的阶乘函数进行测试
var trueFac = fac   // trueFac获得了fac的值,引用、指针
fac = function() {  // 覆盖原fac,如果不使用callee方法,则会影响到trueFac内部,变为return num * 0
  return 0
}
alert(trueFac(5))  // 120
							     // 因为原fac中,使用了callee,所以能返回正常的阶乘数据,
                   // 否则会因为对fac的覆盖,导致trueFac中return num * 0 = 0
alert(fac(5))      // 0

3.6.5.2 this对象: 函数执行的环境对象

window.color = 'red' 
var o = { color: 'blue' } 
function sayColor () {
  alert(this.color)
}
sayColor()            // 'red' - 此处this指向全局环境对象window
o.sayColor = sayColor // o中创建属性sayColor
o.sayColor()          // 'blue' - o中的sayColor属性调用的this,指向的是对象o,故输出blue
// 备注:sayColor依然是一个指针,无论是内部还是外部指向的是同一个指针变量,只是调用时的this对象是不同的

// 更为优雅的调用方法:call()方法

3.6.6 函数的属性和方法

3.6.6.1 length属性

length:希望接收的命名参数的个数(不是arguments的个数)

function foo0 () {
  return ''
}

function foo1 (num) {
  return num
}

function foo2 (num1, num2) {
  return num1 + num2
}

foo0.length // 0
foo1.length // 1
foo2.length // 2

3.6.6.2 prototype属性:apply、call、bind

原型方法                   用途传参arg0传参arg1使用样例
apply()在特定作用域中调用函数,
设置函数体内this对象的值**
对象arguments对象或参数数组sum.apply(this, [arg0, arg1, arg2, ...])
call()在特定作用域中调用函数,
设置函数体内this对象的值
对象逐个列举参数sum.call(this, arg0, arg1, arg2, ...)
bind()创建对象的实例,将需要使用的对象的this绑定到function中对象-sum.bind(this)(10, 20)

注意要点

  1. prototype保存引用类型所有实例方法,包括toString()valueOf()
  2. prototype属性不可枚举,故使用for-in无法发现

1. apply()- 设置函数体内this对象的值

function sum(num1, num2) {
  return num1+ num2
}

// apply的第二个传参是arguments对象或者数组
function callSum1 (num1, num2) {
  return sum.apply(this, arguments)
}
function callSum2 (num1, num2) {
  return sum.apply(this, [num1, num2])
}
callSum1(10, 20) // 30
callSum2(10, 20) // 30

2. call()- 设置函数体内this对象的值

// call方法传递给函数的参数必须逐个列举,而不是传入array
function callSum3 (num1, num2) {
  return sum.call(this, num1, num2)
}
callSum3(10, 20) // 30

3. bind()- 创建函数的实例

// bind方法 - 创建函数的实例
var objSayColor = sayColor.bind(o) // 让objSayColor的this值就是o.this,而非全局的this
objSayColor()   // blue - 全局调用时,objSayColor获取的是o.this
sayColor()      // red -  - 全局调用时,sayColor获取的是window

// bind的传参 - 方法同原函数的传参模式一样
// 样例1:
function sayColorName(title) {
  alert(title + ": " + this.color)
}
var o ={ color: 'blue' }
var objSayColorName = sayColorName.bind(o)('show this color name')
objSayColorName() // show this color name: blue

// 样例2:
function callSum4 (num1, num2) {
  return sum.bind(this)(num1, num2)
}
callSum4(10, 20) // 30

4. 补充: call()apply()的应用 - 扩大作用域:对象无需与方法产生耦合关系

// call和apply的应用 - 扩大作用域 - 对象无需与方法产生耦合关系
window.color = 'red'
var o ={ color: 'blue' }
function sayColor() {
  alert(this.color)
}

sayColor()            // red  - 直接调用:返回全局对象的this.color
sayColor.call(this)   // red  - 传入全局对象this,返回this.color
sayColor.call(window) // red  - 传入全局对象window,返回window.color
sayColor.call(o)      // blue - 传入对象o,返回o.color
/*
 * 普通方法:对象与方法之间产生了耦合关系,对sayColor的覆盖重写等改变都会造成影响
 * o.sayColor = sayColor
 * o.sayColor()
 */

5. 其他方法:valueOf()- 仅返回函数代码

sayColor.valueOf()
/* 输出结果:
 * ƒ sayColor() {
 *   alert(this.color)
 * }
 */

3.6.7 基本包装类型

  1. 基本类型值具备一些包装好的方法供基本类型使用:var s2 = s1.substring(2)
  2. 引用类型与基本包装类型的主要区别:对象的生存期
    1. 用new创建的引用类型对象,在执行流离开当前作用域之前一直都保存在内存中,可在运行时添加属性和方法
    2. 自动创建的基本包装类型对象,只存在于一行代码的执行瞬间,然后立即被销毁,不可在运行时添加属性和方法
// new调用构造函数和直接使用同名的转型函数不一样
var value = '25'
var number = Number(value) // 转型函数
alert(typeof number) // number

var obj = new Number(value) // 构造函数
alert(typeof obj) // object

3.6.7.1 Boolean类型

  1. Boolean类型的实例重写了以下几个方法:

    • valueOf() - 返回基本类型值true/false
    • toString() - 返回字符串‘true'/'false'
  2. 基本类型与引用类型的布尔值的区别

    • typeof操作符对基本类型返回'boolean',对引用类型返回'object'
    • 由于Boolean对象是Boolean类型的实例,所以使用instanceof的时候,对象返回true,基本类型返回false
  3. 建议不要使用实例化方法(new方法),容易产生混淆

3.6.7.2 Number类型

  1. Number类型重写以下几个方法:
    • valueOf() - 返回对象表示的基本类型的数值
    • toString() - 返回字符串形式的数值
  2. 基本类型与引用类型的Number值的区别
    • typeof操作符对基本类型返回'number',对引用类型返回'object'
    • 由于Number对象是Number类型的实例,所以使用instanceof的时候,对象返回true,基本类型返回false
  3. 建议不要使用实例化方法(new方法),容易产生混淆
// Number类型提供的其他方法:
var num1 = 10
var num2 = 10.005

// 1. toFixed - 按照指定的小数位返回数值的字符串表示,且四舍五入
num1.toFixed(2) // 10.00
num2.toFixed(2) // 10.01

// 2. toExponential - 指数表示法
num1.toExponential(1) // 1.0e+1

// 3. toPrecision - 按照何时的方法返回合适的数值格式
var num3 = 99
num3.toPrecision(1) // 1e+2
num3.toPrecision(2) // 99
num3.toPrevision(3) //99.0

3.6.7.3 String类型

  1. 使用构造函数创建
var strObj = new Sting('hello world')
  1. length() - 获得字符串长度
var str = 'hello world'
alert(str.length) // 11
  1. 字符方法
// 1 charAt() - 以单字符串形式返回给定位置的那个字符
var strVal = 'hello world'
alert(strVal.charAt(1))     // e

// 2 charCodeAt - 以字符编码形式返回给定位置的那个字符
alert(strVal.charCodeAt(1)) // 101

// 3 直接使用方括号[]访问给定位置的字符(IE8以前版本不支持)
alert(strVal[1])            // e
  1. 字符串操作方法 - concat()、slice()、substr()、substring()
// 1 concat() - 将一个或多个字符串拼接起来,返回拼接得到的新字符串(注:用+号也有相同效果)
var str1 = 'hello'
var str2 = str1.concat(' world')         // hello world
var str3 = str1.concat(' world', '!')    // hello world!
var str4 = str1 + ' world' + '!'         // hello world!

// 2 slice() - 截取从起始位置到结束位置的字符串
var str5 = strVal.slice(3)               // lo world - 若未指定结束位,则默认为最后一位
var str6 = strVal.slice(3, 7)            // lo w - 指定结束位时,返回字符串不包含最后一位

// 3 substr() - 截取从起始位置到结束位置的字符串
var str7 = strVal.substr(3)              // lo world - 若未指定结束位,则默认为最后一位
var str8 = strVal.substr(3, 7)           // lo worl - 指定结束位时,返回字符串包含最后一位的下一位

// 4 substring() - 截取从起始位置到结束位置的字符串
var str9 = strVal.substring(3)           // lo world - 若未指定结束位,则默认为最后一位
var str10 = strVal.substring(3, 7)       // lo w - 指定结束位时,返回字符串不包含最后一位
  1. 字符串位置方法 - indexOf()、lastIndexOf()
var strVal = 'hello world'

var str11 = strVal.indexOf('o')          // 4 - 从第一位开始往后查询
var str12 = strVal.lastIndexOf('o')      // 7 - 从最后一位开始往前查询
var str12 = strVal.indexOf('o', 6)       // 7 - 从第6位开始往后查询
var str13 = strVal.lastIndexOf('o', 6)   // 4 - 从第6位开始往前查询
  1. trim() - 删除前后空格
// trim() - 删除前后空格
var str14 = ' hello '
var str15 = str14.trim()                 // 'hello'
  1. 大小写转换
var str16 = strVal.toUpperCase()        // HELLO WORLD
var str17 = str16.toLowerCase()         // hello world
  1. 字符串模式匹配方法
// 字符串模式匹配方法 - match()/search()/replace() - 类似调用RegExp的exec()方法,用于匹配正则表达式或RegExp对象
var text = 'cat, bat, sat, fat'
var pattern = /.at/
var matches = text.match(pattern) // 返回一个数组
alert(matches.index) // 0
alert(matches[0]) // 'cat'
alert(pattern.lastIndex) // 0

var pos = text.search(/at/) // 1 - at在text中第一次出现的位置

var rep = text.replace('at', 'ond') // cond, bat, sat, fat - 替换字符串
var rep2 = text.replace(/at/g, 'ond') // cond, bond, sond, fond

var sp = text.split(', ') // ['cat', 'bat', 'sat', 'fat'] - 按符号分割
  1. localeCompare() - 比较两个字符串
// localeCompare() - 比较两个字符串
/* 比较规则:
 * a. 如果字符串在字母表中应该排在字符串参数之前,则返回一个负数(-1)
 * b. 如果字符串等于字符串参数,返回0
 * c. 如如果字符串在字母表中应该排在字符串参数之后,则返回一个正数(1)
 */
var stringValue = 'yellow'
stringValue.localeCompare('bricke') // 1
stringValue.localeCompare('yellow') // 0
stringValue.localeCompare('zooooo') // -1
  1. fromCharCode() - 接收一个或多个字符串编码,并将他们转换成字符串
// fromCharCode() - 接收一个或多个字符串编码,并将他们转换成字符串
String.fromCharCode(104, 101, 108, 108, 111) // hello 

笔记目录:

JavaScript学习(1) - JavaScript历史回顾

JavaScript学习(2) - 基础语法知识

JavaScript学习(3)- 聊聊原型链- 1. 变量

JavaScript学习(3)- 聊聊原型链- 2. 对象与原型

JavaScript学习(3)- 聊聊原型链- 3. 原型链与继承

JavaScript学习(4)- 聊聊闭包那些事

Github:

Github笔记链接(持续更新中,欢迎star,转载请标注来源)