你不知道的JavaScript(上卷)(读书笔记之一)

你不知道的JavaScript(上卷)(读书笔记之一)

第二部分 this 和对象原型

任何足够先进的技术都与魔法无异。
—Arthur.C Clarke

第1章 关于this

1.this到底是什么
this是**在运行时**进行绑定的,并不是在编写时绑定的,它的上下文取决于行数调用时的各种条件。
this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。
this实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。

在这里插入图片描述
在这里插入图片描述

第2章 this全面解析

2.如何判断this
可以从以下方面进行判断:
  1. 函数是否在new中调用(new绑定)?如果是的话this绑定的是新创建的对象。var bar = new foo()
  2. 函数是否通过call、apply(显示绑定)或者硬绑定调用?如果是的话,this绑定的是指定的对象。var bar = foo.call(obj2)
  3. 函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this绑定的是哪个上下文对象。var bar = obj1.foo()
  4. 如果都不是的话,使用默认绑定。如果再严格模式下,就绑定到undefined,否则绑定到全局对象。var bar = foo()

在这里插入图片描述

3. 被忽略的this

如果你把null或者undefined作为this的绑定对象传入call、apply或者bind,这些值在调用时会被忽略,实际应用的是默认绑定规则:

function foo() {
	console.log(this.a)
}
var a = 2
foo.call(null) // 2
那么什么情况下会传入null呢?

一种是非常常见的做法是使用apply(..)来“展开”一个数组,并当作参数传入一个函数。另一种是bind(..)可以对参数 进行柯里化(预先设置一些参数):

function foo(a,b) {
	console.log('a:' + a, 'b:' + b)
}
// 把数组“展开”成参数
foo.apply(null, [2, 3]) // a:2 b:3

//使用bind(..)进行柯里化
var bar = foo.bind(null, 2)
bar(3) // a:2 b:3

这两种方法都需要传入一个参数当作this的绑定对象。如果不关心this的话,你仍然需要传入一个占位符,这时null可能是一个不错的选择,如上。
在这里插入图片描述

4. 更安全的this

JavaScript中创建一个空对象最简单的方法是Object.create(null)Object.create(null){}很像,但是并不会创建Object.prototype这个委托,所以它比{}“更空”。
在这里插入图片描述

赋值表达式 p.foo = o.foo的返回值是目标函数的引用,因此调用对象的位置是foo()而不是p.foo()或者o.foo()。使用的是默认绑定规则。

function foo() {
	console.log(this.a)
}
var a = 2
var o = { a: 3, foo: foo }
var p = { a: 4 }
o.foo() // 3
(p.foo = o.foo)() // 2

在这里插入图片描述

第3章 对象

3.1 语法

对象定义:声明(文字)形式和构造形式

  1. 文字形式
var myObj = {
	key: value
	//...
};
  1. 构造形式
var myObj = new Object();
myObj.key = value;

区别:
二者产生的对象是一样的。唯一区别是文字形式可以一次性添加多个属性,而构造形式只能逐个添加属性。

3.2 类型
  • 基本类型: StringNumberBooleanUndefinedNullSymbol
  • 引用数据类型:Object
    注:Symbol 是 ES6 引入了一种新的原始数据类型,表示独一无二的值。
    注:有一种错误的说法。“JavaScript 中万物皆是对象”,这显然是不对的,因为基本类型本身并不是对象。对于typeof null 会返回object,那是语言本事的一个bug,二进制前三位都是0的话会被判为object类型,而null的二进制是32个0,所以类型是object.
内置对象

JavaScript中有一些对象子类型,通常被称为内置对象

  • String
  • Number
  • Boolean
  • Object
  • Function
  • Array
  • Date
  • RegExp
  • Error
    在JavaScript中它们实际上是一些内置函数。可以被当做构造函数来使用(由new产生的函数调用)。
var str = 'I am a string';
var strObject = new String(str);
typeof strObject; // "Object"
strObject instanceof String; // true

通过以下方法可以检查对象子类型

Object.prototype.toString.call(new Date()); // [object, Date]
Object.prototype.toString.call({}); // [object, Object]
Object.prototype.toString.call(3); // [object, Number]
3.3 内容
  • 在对象中,属性名永远都是字符串。
  • 若不是字符串,则首先会先被转换成字符串。
var myObject = {};
myObject[true] = 'foo';
myObject[3] = 'bar';
myObject[myObject] = 'baz';

myObject['true']; // 'foo'
myObject['3']; // 'bar'
// 对象转成字符串是 '[object, Object]'
myObject['[object Object]']; // 'baz'
3.3.3 数组

数组也是对象,可以给数组添加属性。添加属性length的值不会变。

var myArr = ['foo', 'bar'];
myArr.zzz = 'zzz';
myArr.length; // 2
myArr.zzz; // 'zzz'

注意:如属性名“看起来”想一个数字,那它会变成一个下标,此时就不是添加一个属性了。

var myArr = ['foo', 'bar'];
myArr[1]; // 'bar'
myArr['1'] = 'change';
myArr['2'] = 'add';
myArr[1]; // 'change'
myArr.length; // 3
3.3.5 属性描述符

ES5开始,所有属性都具备了属性描述符。

var myObj = { a: 1 };
Object.getOwnPropertyDescriptor(myObj, 'a');
// {
// 	value: 1,
// 	configurable: true,
// 	enumerable: true,
// 	writable: true
// }
  • 通过Object.getOwnPropertyDescriptor函数获取属性描述符。configurable(可配置)、enumerate(可枚举)、writable(可写)。
  • 可通过Object.defineProperty函数去配置这些属性
  1. writable 决定是否可以修改属性的值。
    可以把writable:false看成敌营了一个空的操作settersetter被调用时抛出一个TypeError错误。

  2. configurable 决定是否可以用defineProperty(..)方法修改属性描述符。

    • configurable设置成false,是单向操作,无法撤销的。
    • configurable为flase时,还是可以把writabletrue设置为false,但是无法从false改为true
    • 除了禁止修改属性,也禁止delete删除属性。
  3. enumerate,设置false,则不会出现再for..in循环里。

3.3.6 不变性
  1. 对象常量
    结合writable:falseconfigurable:false创建一个常量(不可修改、重定义和删除)。
  2. 禁止扩展
    禁止添加新的属性并且保留已有属性,可以使用Object.preventExtensions( myObj )
  3. 密封
    禁止添加新的属性、不能重新配置、不能删除任何现有属性,但是能修改属性。可以使用Object.seal,实际上它对现有的对象调用Object.preventExtensions( myObj ),并且对每个属性设置configurable:false
  4. 冻结
    禁止添加新的属性、不能重新配置、不能删除任何现有属性、不能修改属性。可以使用Object.freeze,实际上它对现有的对象调用Object.seal( myObj ),并且对每个属性设置writable:false
3.3.7 [[Get]] 操作
var myObj = {
	w: 1
};
myObj.w; // 1
  • myObj对象访问属性w,实际上是实现了[[Get]]操作,首先在对象中找相同的属性,没找到的话回去[[Prototype]]链上找,找不到返回undefined
  • 跟普通的访问变量不同,访问变量的话找不到没有定义的是会抛出一个ReferenceError异常。
3.3.10 存在性
var myObj = {
	w: 2
};
Object.prototype.test = 1;
'w' in myObj; // true
myObj.hasOwnProperty('w'); // true

'test' in myObj; // true
myObj.hasOwnProperty('test'); // false
  • in操作符会检查属性是否在对象及其[[Prototype]]原型链中,
  • hasOwnProperty方法只会检查属性是否在对象中。
var myObj = {};
Object.defineProperty(myObj, 'a', {
	enumerable: true, // a 可枚举的
	value: 2 // 值
})
Object.defineProperty(myObj, 'b', {
	enumerable: false, // b 不可枚举的
	value: 1 // 值
})

myObj.hasOwnProperty('a'); // true
myObj.hasOwnProperty('b'); // true
'a' in myObj; // true
'b' in myObj; // true
for(var key in myObj) {
	console.log(key, myObj[key])
}
// a 2

myObj.propertyIsEnumerable('a'); // true
myObj.propertyIsEnumerable('b'); // false

Object.keys(myObj); // ['a']
Object.getOwnPrepertyNames(myObj); // ['a', 'b']
  • 可以看到enumerate:false,还是可以用hasOwnPropertyin操作符判断是否存在,但是在for..in遍历中显示不出来,可枚举相当于可以出现在对象属性的遍历中
  • propertyIsEnumerable会检查属性名是否直接在对象上(不会检查[[Prototype]]原型链)并且满足enumerate:true
  • Object.keys(myObj),返回所有可枚举的属性(不查找[[Property]]原型链)。
  • Object.getOwnPrepertyNames(myObj),返回所有属性,不管是否可枚举(不查找[[Property]]原型链)。
相关

个人博客网站(https://wenbintian.gitee.io/blog/)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值