ES5补充笔记
对象定义
Object.create(src): 创建一个新对象,并继承src对象现有的成员。
Object.assign(): 浅拷贝对象
let will = { age:41,name:"Will Smith",family:{ member:[] }};
let clone = Object.create(will);
console.log(clone); // { age:41,name:"Will Smith",family:{ member:[] }};
console.log(will === clone); // false;
原型与原型链
在java ,c++, c# 等一些强类型的语言中,一个类可以通过 extends 关键字继承另一个类,从而实现对目标类进行扩展,逻辑复用的功能;
es6 以前, javascript 中还没有类的具体定义,但是,js提供一个原型链对象来模拟继承特性, 当obj1 继承 obj2后,obj1原型对象就是。java中可以有多层继承,同样,js中也允许原型多层引用,有多个对象之间的继承关系形成的多层原型链的引用关系通常称之为对象的**原型链
**。
-
对象通过__proto__可以读取原型对象;
__proto__
并不是语言本身的特性,这是各大厂商具体实现时添加的私有属性,虽然目前很多现代浏览器的 JS 引擎中都提供了这个私有属性,但依旧不建议在生产中使用该属性,避免对环境产生依赖。生产环境中,我们可以使用Object.getPrototypeOf
方法来获取实例对象的原型,然后再来为原型添加方法/属性。 -
一个对象除了拥有自身定义的属性,方法外,还具有他所在的原型链的所有上游对象的属性和方法。
let will = { age:41,name:"Will Smith",family:{ member:[] }}; let clone = Object.create(will); console.log(will === clone.__proto__) // true;
可以通过object.isPrototypeOf()方法判断对象是不是在某个对象的原型链上:
console.log( will.isPrototypeOf(clone) ); // true;
但是在js中,原型属性是可以随意更改的:
clone.__proto__ = null; console.log(clone.age); // undefined;
如果我们手动修改了原型链,那么 isPrototypeOf 可能会失效:
clone.__proto__ = null; clone.__proto__ = will; console.log( will.isPrototypeOf(clone) ); // false;
正确的做法是使用Object.setPrototypeOf():
Object.setPrototypeOf(clone,will); console.log( will.isPrototypeOf(clone) ); // true;
-
函数通过 函数名.prototype读取它的原型对象, 一个函数的原型链上所有打的属性,方法都会被这个函数的子类或者这个通过函数作为构造器new出来的对象继承;
通常,我们将函数的prototype上的方法或者属性称之为它的静态方法/属性
function WillSmith() { this.age = 41; }; WillSmith.prototype.kill = function() { console.log("mama, i just killed a man ...") }; let junior = new WillSmith(); junior.kill(); // mom, i just killed a man ...
Object.create方法实际上是将 传入的对象直接放到被创建的对象的原型上,
let obj = {x:1};
let obj1 = Object.create(obj);
// 相当于
let obj1 = {};
Object.setPrototypeOf(obj1,obj);
对象属性定义
Object.defineProperty(),Object.defineProperties()
Object.defineProperty() 用于定义或者修改对象成员属性的特性;
let obj = Object.defineProperty({},"age",{
writeable:false, // 属性是否可以被修改 ,默认为false
enumerable:false, // 属性是否可以被迭代器(for ... in 循环)读取,默认为false
configurable:false, // 属性是否能被Object.defineProperty重新定义,以及能否删除属性。默认为false
value:18, // 初始化赋值,
get:function(target) {}, // 不能与 writeable 和 value 一起使用
set: function(value,target) {}, // 不能与 writeable 和 value 一起使用
});
Object.defineProperty(obj,"name",{
writeable:true,
enumerable:true,
configurable:true,
value:"John" // 初始化赋值
});
obj.age = 19; // 不会报错,但是修改不生效
console.log(obj.age); // 18;
Object.keys(obj); // ["name"]
对象属性保护
Object.preventExtensions():禁止添加属性,同时保留现有的属性( 可以删除属性 )
Object.seal() :禁止添加属性,禁止重新定义或删除现有的属性, 相当于 调用了Object.preventExtensions(),并把所有的属性的configurable标记为false
与 Object.freeze(): 完全冻结对象,无法添加删除属性,无法重新定义属性,无法修改属性值。这个方法实际上会在一个现有对象上调用Object.seal(),并把所有现有属性标记为writable: false,这样就无法修改它们的值。
对象属性读取器
Object.entries(),Object.keys(),Object.values(),Object.fromEntries(),Object.getOwnPropertyNames(),Object.getOwnPropertyDescriptor()
修改函数执行上下文
apply(),bind(),call() 三个方法都可以修改函数执行时的this指向,apply() 和 call() 用法相同,不同的时参数传递,apply() 和 call(),bind() 的第一个参数都新的this指向的对象,call()调用方法时,方法的参数直接跟在call的第一个参数后面,直接用**,
**隔开,而apply 调用时,参数需要封装到数组中传递:
var x = 1,y = 2;
function testAdd(base, unit) {
return this.x + this.y + (base || 0) + (unit || "");
}
let obj = { x:100,y:20 };
console.log(testAdd(2,"em")); // 5em;
console.log(testAdd.call(obj,2,"em")) // 122em
console.log(testAdd.apply(obj,[10,"px"])) // 130px
bind() 的用法和call() 一样,不同的是,bind方法调用后返回一个新的函数,可以认为是目标函数的代理:
console.log(testAdd.bind(obj,10,"px")()); // 130px
// 或者
console.log(testAdd.bind(obj)(10,"px"));
对象属性状态获取
Object.is(),isExtensible(),isFrozen(),isSealed()
null , undefined 与 NaN
null 和 undefined 都是javascript 中的 primitive value
,他们都可以标识目标对象没有值,不同的是 undefined 一般表示属性被定义,但是没有赋值。null 一般表示,属性定义了,并且人为的赋予了一个空值,带有一点业务含义在里面。
NaN 也是javascript 中的 primitive value
, 他表示的意义为“不是数字”,通常不会手动给一个变量赋值NaN, 而是会在异常的数学运算中产生。
null
null 的类型object, 他可以直接转换成某些类型
-
null 与数字做运算时,自动抓换为0
var a = 0; console.log(a + 1); // 1 console.log(a - 1); // -1 console.log(a * 1);// 0
-
null 与 bool值运算或者与逻辑运算符运算时,会视为 false
console.log(!null) // true; console.log(null && true); // false
-
null 与 字符串做运算会转成 “null”:
console.log(null + "-haha") // null-haha
undefined
- undefined 与数字运算时结果必然时NaN,
- null 与 字符串运算时,表现与null类似,会转成 “undefined”字符串,与bool值运算时则与null一致
NaN
NaN的类型时number,但是NaN与任何数字都不等,甚至都不等于NaN。NaN在与bool值做布尔运算时,当成 false处理,
严格模式
严格模式使得Javascript在更严格的语法检查下运行。由于语法检查规则不一样,同样的代码在普通模式能执行,在严格模式下可能会报错。
通过在函数挥着脚本的第一行使用一个字符串字面量 ‘use strict’; 开启
'use strict';
或者:
function hello() {
'use strict';
}
严格地说,只要前面不是产生实际运行结果的语句,"use strict"可以不在第一行,比如直接跟在一个空的分号后面
严格模式语法变化
-
变量使用
在严格模式中,变量必须申明才能使用
'use strict'; foo = 'bar'; // Uncaught ReferenceError: foo is not defined
-
eval函数的作用域
正常模式下,Javascript语言有两种变量作用域,全局作用域和函数作用域, eval函数执行语句的作用域取决于eval函数本身所处的作用域。而在严格模式中,引入第三种作用域:eval作用域; 严格模式下,eval函数执行的语句的作用域为一个独立的作用域,语句申城的变量只属于eval内部,不在能生成全局变量。
'use strict'; eval("var b = 2;console.log(b);") // 2 console.log(b); // Uncaught ReferenceError: b is not defined
-
禁止this指向全局对象
(function hello() { console.log(this); // 浏览器中为 window 对象 })(); (function hello() { "use strict"; console.log(this); // undefined })();
-
禁止读取函数调用栈相关内部变量
(function hello() { "use strict"; console.log(hello.caller); // 报错 console.log(hello.callee); // 报错 console.log(hello.arguments); // 报错 TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them })();
-
运行时报错
普通模式下,有些操作会静默失败,但是不会报错,例如 删除一个不可配置的属性,给只读属性复制,修改冻结对象等操作。在严格模式下,这些操作都会在运行时报错。
严格模式下只能删除 configurable 为 true 的对象属性,删除configurable 为false 的属性会报错:
(function() { var obj = Object.defineProperties({},{ "a":{ value:1, configurable:false, }, }); delete obj.a; // 不报错 console.log(obj); // { a: 1 } })(); (function() { var obj = Object.defineProperties({},{ "a":{ value:1, configurable:false, }, }); delete obj.a; // Cannot delete property 'a' of #<Object> })();
严格模式下对一个只读属性赋值也会显示报错:
(function () { "use strict"; var obj = Object.defineProperty({}, "a", { value: 1, writable: false }); obj.a = 2; // Cannot assign to read only property 'a' of object '#<Object>' })();
-
严格模式下,对象不能有命名重复的属性,函数不能有重复名称的参数,否则会报错
-
禁止八进制表述法
正常模式下,整数的第一位如果是0,表示这是八进制数,比如0100等于十进制的64。严格模式禁止这种表示法,整数第一位为0,将报错。
-
禁止在非函数体的块级作用域内申明函数