前言:
本文只为自己最近看了很多关于es5对象方面的视频以及书籍,深感前端之路路漫漫其修远兮,才写了此篇博客,也是鄙人第一次写博客,写的不好大家勿怪。另外本文总结的是es5方面的知识点,就不会用到什么let,const以及class之类的es6语法,为了与标题保持一致。我也会在之后总结es6以及typescript方面的知识。还有本文的所有代码都是亲测运行有效后才复制上来的,基本不会存在报错的情况,如果有错误或者代码哪儿有问题还望各位大佬多多指教,毕竟我也只是正在前端学习的路上而已,也不是什么大牛。代码并非是书上的源码,我写的比较简单,原因是照顾一些初学者。如果你在作者的文章中学到了一些新知识不仿给作者点个赞吧,在这里先谢谢了。
对象的定义:
无序属性的集合(简单易懂一点,不喜欢扯太复杂,也不喜欢拖泥带水)
重要概念:
- 私有变量:对于对象而言,所有的属性都是公有的,严格来讲是没有私有属性的说法的,但变量是有私有的。
- 私有方法:在构造函数内部不能为外界访问,只能通过函数内部方法访问的方法。
- 公有属性:在构造函数内部,可以为外界直接访问的属性。
- 公有方法:在构造函数内部,可以为外界直接访问的方法。
- 特权方法:可以访问构造函数中私有属性的方法。
- 静态属性和静态方法:无需实例化就可以调用的属性和方法就叫静态属性和静态方法。
- 实例:程序使用类创建对象时,生成的对象叫作类的实例
- 实例化:由类创建对象实例的过程叫做实例化
- 原型链:当访问某个对象的属性时,如果该对象没有此属性,则会到他的原型中去查找,它的原型属性中又有自己的原型属性,一直这样查找下去,这就构成了原型链(仅代表个人理解,并未找到参考资料)
- 原型:对象中固有的__proto__属性,该属性指向对象的prototype原型属性。
- this指向:正常来讲this指向其执行上下文环境,但this的指向也不是固定不变的,后面会深入讨论this的指向问题。
- 闭包:是可访问上一层函数作用域里变量的函数,即便上一层函数已经关闭。
- 递归:递归最简单的解释就是自己调用自己
- 作用域:window中声明的变量是全局变量,全局变量处在全局作用域中,函数中声明的变量是局部变量,局部变量在函数作用域中。(另外在es6用let和const在代码块中声明的变量具有块级作用域)
- 作用域链:函数访问某一个变量值时,此函数会在该函数中寻找该变量,如果没有则到函数外寻找,最终会到window中寻找,这就形成了作用域链。
- 浅拷贝:只复制一层对象属性,只是将数据中存放的引用拷贝下来,但依旧指向同一个存放地址。
- 深拷贝:则递归复制了所有层级,将数据中所有的数据都拷贝下来,而不仅仅是引用。拷贝下来的数据的修改,并不会影响原数据。
对象的声明方式:
- 字面量方式声明:
优缺点:简单快捷,使用一个接口创建对象会产生大量重复代码
var obj = {};
- 构造函数方式声明:
优缺点:可以标识对象的类型,但每个方法都会在实例上重新创建一遍
var Obj = function(name,age,sex ){
this.name = name;
this.age = age;
this.sex = sex;
}
var newObj = new Obj('张三',24,'男');
console.log(newObj);
console.log(newObj.constructor == Obj);//constructor构造器
console.log(newObj instanceof Object);//instanceof判断是否为该对象的子类
console.log(newObj instanceof Obj);
var o = new Object;
Obj.call(o,'李四',26,'女');
console.log(o.name);
- 工厂模式声明:
优缺点:可以创建多个相似对象,但未解决对象类型的识别问题
var obj = function(name,age,sex){
var o = new Object() || {};//new写烦了,试了一下{}创建,也没有问题。
o.name = name;
o.age = age;
o.sex = sex;
return o;
}
var newObj = obj('张三',24,'男');
console.log(newObj);
- 原型模式声明:
优缺点:它的优点不仅能自定义对象类型,更重要的是可以在所有引用类型(Object、Array、String)中创建新方法,虽然也可以修改,但并不推荐。原型中很多实例被共享,对于引用类型值来说问题就比较突出。
var Obj = function(){};
Obj.prototype.name = '张三';
Obj.prototype.age = 24;
Obj.prototype.sex = '男';
var o = new Obj;
console.log(o.name);
console.log(Obj.prototype.isPrototypeOf(o));//判断对象的原型prototype是否指向Obj;
console.log(Object.getPrototypeOf(o)==Obj.prototype);//同Obj.prototype
console.log(Obj.hasOwnProperty('name'));//在该对象实例中是否有该属性,有就返回true
console.log('name' in Obj);//在该对象中是否有该属性,不管是原型上还是实例上,有就返回true
console.log(o.hasOwnProperty('name'));
console.log('name' in o);
更简单的方式:
var Obj = function(){};
Obj.prototype={
name : '张三',
age : 24,
sex : '男'
}
- 动态原型模式:
优缺点:集合了构造函数和原型的优点,但不能使用对象字面量重写原型
var Obj = function(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
if(typeof this.sayName != 'function'){
Obj.prototype.sayName=function(){
console.log(this.name);
}
}
}
var o = new Obj('张三',24,'男');
o.sayName();
- 寄生构造函数模式:
优缺点:优点太过奇特了,有点儿说不上来,instanceof检测不到其类型
var Obj = function(){
var values = new Array();
values.push.apply(values, arguments);
values.toPipedString = function(){
return this.join("|");
};
return values;
}
var o = new Obj("red", "blue", "green");
console.log(o.toPipedString());
- 稳妥的构造函数模式:
优缺点:没有任何除了定义的get方法访问的到其属性,具有一定的安全性,与构造函数之间没有太大的关系
var Obj = function(name,age,sex){
var o = new Object();
o.getName = function(){
console.log(name);
}
return o;
}
var newObj = Obj('张三',24,'男');
newObj.getName();
- 混合模式:
注:以构造函数和原型模式组合为例。
优缺点:可以结合其他模式的优点,缺点目前没发现
var Obj = function(name,sex,age){
this.name = name;
this.sex = sex;
this.age = age;
}
Obj.prototype = {
getName:function(){
console.log(this.name);
}
}
var o = new Obj('李四',24,'男');
o.getName();//李四
对象的特性:
前言:众所周知对象有三个特性,也是比较重要的特性——封装、继承、多态。
封装:封装就是有些私有的属性和方法,用户只能通过公有方法去访问这些私有属性。
注:文档开头已注明了基本概念
var Obj = function(name){
var name = name;//私有变量
this.age = 26;//公有属性
this.getName = function(){//特权方法
return name;
};
Introduce = ()=>{//私有方法
console.log(this);
//如果不用es6的箭头函数,那么this指向的是window,this.age始终未定义,es6箭头函数指向它所在的父级作用域,this.age就会获取到26的值(实在不得已破了个例)
return name + '今年' + this.age + '岁了';
};
this.getIntroduce = function(){//公有方法
return Introduce;
}
}
var o = new Obj('张三');
console.log(o);//Obj{age: 26, getName: ƒ}
console.log(o.name);//undefined
console.log(o.getName());//张三
// console.log(o.Introduce());//会报错
var newGetIntroduce = o.getIntroduce();
console.log(newGetIntroduce());
多态:多态就是构造函数根据传入的变量不同,对同一方法,有不同的返回结果
var Obj = function(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
var newObj1 = new Obj('张三',24,'男');
var newObj2 = new Obj('李四',18,'女');
console.log(newObj1);
console.log(newObj2);//多态指得就是调用同一个函数产生不同对象的这种特性
继承:一个对象继承另一个对象的属性以及方法(最重要)
- 原型链方式继承:
var Obj = function(){
var name = '张三';
this.getName = function(){
return name;
};
};
var newObj = function(){};
newObj.prototype = new Obj();//实现继承
var O = new newObj();
console.log(O.getName());
- 构造函数方式继承:
var Obj = function(name){
this.name = name;
};
var newObj = function(){
Obj.call(this,'张三');
};
var O = new newObj();
console.log(O.name);//张三
- 组合方式继承:(原型链+call)
var Obj = function(){
this.name = '张三';
}
Obj.prototype.getFn = function(){
return this.name;
}
var newObj = function(){
Obj.call(this);//继承Obj函数内部this上的值
}
newObj.prototype = new Obj();//继承Obj函数原型链上的值
var o = new newObj();
console.log(o.getFn());//张三
- 寄生继承:
var newObj = function(Obj){
var o = Object.create(Obj);//实现继承
o.getName = function(){
return o.name;
}
return o;
}
var Obj = {
name:'张三'
}
var Obj1 = newObj(Obj);
console.log(Obj1.getName());//张三
- 寄生组合式继承:
var Obj = function(){
this.name = '张三';
}
Obj.prototype.getName = function(){
return this.name;
}
var newObj = function(){
Obj.call(this);//获取对象中的自身属性值
}
var inherit = function(Obj,newObj){
var prototype = Object.create(newObj.prototype);//获取子类的原型
prototype.constructor = Obj;//将父对象放置在子对象的constructor下
Obj.prototype = prototype;//将父对象的原型赋值给子对象的原型
}
inherit(newObj,Obj);
var O = new newObj();
console.log(O.getName());//张三
- call()/apply()方法继承:
//call()
var Obj = function(){
this.name = '张三';
}
var newObj = function(){
Obj.call(this);//实现继承
this.getName = function(){
return this.name;
}
}
var o = new newObj();
console.log(o.getName());//张三
//apply()
var Obj = function(){
this.name = '张三';
}
var newObj = function(){
Obj.apply(this);//实现继承
this.getName = function(){
return this.name;
}
}
var o = new newObj();
console.log(o.getName());//张三
- 对象冒充:
var Obj = function(){
this.name = '张三';
}
var newObj = function(){
this.Obj = Obj;
this.Obj();//实现继承
delete this.Obj;
this.getName = function(){
return this.name;
}
}
var o = new newObj();
console.log(o.getName());
- 原型式继承:
var inherit = function(o){//实现继承的函数
function F(){};
F.prototype = o;
return new F();
}
var Obj = {
name:'张三'
}
var newObj = inherit(Obj);
console.log(newObj.name);//张三
this指向问题:
- 在js文件中直接打印this,打印结果为window:
console.log(this);//window
- 在函数中打印this,打印结果为Window:
function fn(){
console.log(this);//Window
};
fn();
- 在对象中打印this,this的指向为该对象:
var Obj = {
_this : function(){
return this;
}
}
console.log(Obj._this());//Obj
- 如果是绑定的dom事件,该事件方法中的this指向该dom元素:
//html部分
<div id="v1" style="width: 200px; height: 200px;background-color: #666;position: absolute;top:50%;left: 50%;transform:translate(-50%,-50%);"></div>
//js部分
var v1 = document.getElementById('v1');
v1.onclick = function(){
console.log(this);
}//返回结果为html部分全部内容
小结:综上所述,this一般情况下指向其执行的上下文环境(es6箭头函数中this指向其父级作用域)
那么如何改变this的指向呢?
- 通过call方法来改变其this指向:
function fn(){
console.log(this);
};
var Obj = {
name:'李四',
getThis:function(){
console.log(this);
}
}
fn();//Window {parent: Window, opener: null, top: Window, length: 0, frames: Window, …}
fn.call(Obj);//{name: "李四", getThis: ƒ}
- 通过apply方法来改变其this指向:
function fn(){
console.log(this);
};
var Obj = {
name:'李四',
getThis:function(){
console.log(this);
}
}
fn();//Window {parent: Window, opener: null, top: Window, length: 0, frames: Window, …}
fn.apply(Obj);//{name: "李四", getThis: ƒ}
- 通过bind方法来改变其this指向:
function fn(){
console.log(this);
};
var Obj = {
name:'李四',
getThis:function(){
console.log(this);
}
}
fn();//Window {parent: Window, opener: null, top: Window, length: 0, frames: Window, …}
fn.bind(Obj)();//{name: "李四", getThis: ƒ}
作用域和作用域链:
作用域:在es5中有全局作用域和函数作用域。
全局作用域:在函数作用域中,能访问到全局作用域中的变量
var num = 1;
function fn(){
console.log(num);//1
}
fn();
函数作用域:在函数中的变量,在函数外部的全局作用域中是访问不到的
function fn(){
var num = 1;
console.log(num);//1
}
fn();
console.log(num);//报错:num is not defined
一种比较特殊的情况:若变量没有用关键字进行声明,那么默认是全局变量。
function fn(){
num = 1;
}
fn();
console.log(num);//1
//另外说一下,这种写法在严格模式下会报错。
这种写法虽然可以能在全局作用域中拿到函数中的变量,但一般不推荐这么写。
在es5中没有块级作用域,然而在许多情况下会产生一定的问题:
for(var i=0;i<2;i++){
console.log(i);//0,1;
}
console.log(i);//2
在还没有let和const的块级作用域时,我们的前辈已经想到了用私有作用域来模仿块级作用域:
(function(){
for(var i=0;i<2;i++){
console.log(i);//0,1;
}
})();
console.log(i);//i is not defined
这样外界在for循环结束后就访问不到i变量了。
小结:如果要通过一个标准的方法访问函数中的变量的话,推荐使用闭包。
闭包:
- 基本概念:是可访问上一层函数作用域里变量的函数,即便上一层函数已经关闭。(个人理解:可以访问函数中局部变量的函数叫做闭包)
- 使用场景:当我们需要在模块中定义一些变量,并希望这些变量一直保存在内存中但又不会 “污染” 全局的变量时,就可以用闭包来定义这个模块。
- 代码示例:
function fn(){
var name = '张三';
var age = 20;
return function(){
return name + '今年' + age + '岁了';
}
}
var newFn = fn();
console.log(newFn());//张三今年20岁了
- 闭包的优缺点:
优点:1:变量长期驻扎在内存中;2:避免全局变量的污染;3:私有成员的存在 ;
缺点:1.导致内存泄漏;2.会改变父函数内部变量的值
产生内存泄漏的原因: 如果一个对象不再被引用,那么这个对象就会被 GC回收,否则这个对象一直会保存在内存中(两个对象相互引用也会被回收,另外提一句谷歌v8引擎下是分代式回收机制:新生代和老生代) - 解决闭包内存泄漏的方法:在退出函数之前,将不使用的局部变量全部删除。
- 垃圾回收策略:标记清除(较为常用)和引用计数。
递归:
- 基本概念:函数调用自身的函数名造成的
- 代码示例:
function fn(n){
if(n <= 1){
return 1;
}else{
return n*fn(n-1);
}
}
console.log(fn(10));//3628800
3.递归的优缺点:
优点:在树的前序,中序,后序遍历算法中,递归的实现明显要比循环简单得多。
缺点:
- 递归由于是函数调用自身,而函数调用是有时间和空间的消耗的:每一次函数调用,都需要在内存栈中分配空间以保存参数、返回地址以及临时变量,而往栈中压入数据和弹出数据都需要时间。->效率
- 递归中很多计算都是重复的,由于其本质是把一个问题分解成两个或者多个小问题,多个小问题存在相互重叠的部分,则存在重复计算,如fibonacci斐波那契数列的递归实现。->效率
- 调用栈可能会溢出,其实每一次函数调用会在内存栈中分配空间,而每个进程的栈的容量是有限的,当调用的层次太多时,就会超出栈的容量,从而导致栈溢出。->性能
深浅拷贝:
- 浅拷贝:
基本概念:只复制一层对象属性,只是将数据中存放的引用拷贝下来,但依旧指向同一个存放地址。
实现方式:
(1). Object.assign():
let a = {
age: 1
}
let b = Object.assign({}, a)
a.age = 2
console.log(b.age)//1
(2). 通过展开运算符 ... 来实现浅拷贝:
let a = {
age: 1
}
let b = { ...a }
a.age = 2
console.log(b.age) // 1
- 深拷贝:
基本概念:则递归复制了所有层级,将数据中所有的数据都拷贝下来,而不仅仅是引用。拷贝下来。
实现方式:
JSON.parse(JSON.stringify(object)):(不止这一个,不过其他的不太好记,暂时只写这一个了)
let a = {
age: 1,
jobs: {
first: 'FE'
}
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) // FE
缺点:会忽略 undefined、不能序列化函数、不能解决循环引用的对象(会忽略es6的symbol)
皮一下:你以为这就完了?不这才刚刚开始,接下来将一一列举Object的方法以及属性!(可能有的能容会与之前的内容有所重复)
Object的属性与方法列举:(力求做到简单易懂)
前言:如果有没有列举到的属性以及方法欢迎补充
先看一下基本目录吧:
(这是官方解释,后面我会根据自己的理解给大家简单的解释一下)
属性:
- Object.prototype:属性表示 Object 的原型对象
- Object.prototype.constructor:所有对象都会从它的原型上继承一个 constructor 属性
方法:
- Object.assign():用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。
- Object.create():创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
- Object.defineProperties():直接在一个对象上定义新的属性或修改现有属性,并返回该对象。
- Object.defineProperty():会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
- Object.entries():返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for...in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环还会枚举原型链中的属性)。
- Object.values()方法返回一个给定对象自身的所有可枚举属性值的数组,值的顺序与使用for...in循环的顺序相同 ( 区别在于 for-in 循环枚举原型链中的属性 )。
- Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和使用 for...in 循环遍历该对象时返回的顺序一致 。
- Object.freeze():可以冻结一个对象。
- Object.fromEntries() 方法把键值对列表转换为一个对象。
- Object.getOwnPropertyDescriptor() 方法返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)
- Object.getOwnPropertyDescriptors() 方法用来获取一个对象的所有自身属性的描述符。
- Object.getOwnPropertyNames()方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组。
- Object.getOwnPropertySymbols() 方法返回一个给定对象自身的所有 Symbol 属性的数组。
- Object.getPrototypeOf() 方法返回指定对象的原型(内部[[Prototype]]属性的值)。
- Object.is() 方法判断两个值是否是相同的值。
- Object.isExtensible() 方法判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)。
- Object.isFrozen()方法判断一个对象是否被冻结。
- Object.isSealed() 方法判断一个对象是否被密封。
- Object.preventExtensions()方法让一个对象变的不可扩展,也就是永远不能再添加新的属性
- hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。
- isPrototypeOf() 方法用于测试一个对象是否存在于另一个对象的原型链上。
- propertyIsEnumerable() 方法返回一个布尔值,表示指定的属性是否可枚举。
- toLocaleString() 方法返回一个该对象的字符串表示。此方法被用于派生对象为了特定语言环境的目的(locale-specific purposes)而重载使用。
- toString() 方法返回一个表示该对象的字符串。
- valueOf() 方法返回指定对象的原始值。
- Object.seal()方法封闭一个对象,阻止添加新属性并将所有现有属性标记为不可配置。当前属性的值只要原来是可写的就可以改变。
具体解析:
- Object.prototype:可以在已知对象上添加相应的属性以及方法,甚至是修改对象原型上的的方法。但是并不推荐修改对象原型上的方法,因为这样做有可能会导致不可预测的错误。
var Obj = function(name){
this.name = name;
this.isShow = true;
};
Obj.prototype.age = 23;
Obj.prototype.getName = function(){
if(this.isShow){
console.log('我的名字是' + this.name + ',今年' + this.age + '岁');
}
}
var newObj = new Obj('张三');
newObj.getName();//我的名字是张三,今年23岁
- Object.prototype.constructor:所有对象都会从它的原型上继承一个 constructor 属性
var Obj = function(name){
this.name = name;
}
var newObj = new Obj('张三');
console.log(newObj.constructor);//ƒ (name){this.name = name; }
- Object.assign():这个方法绝大多数人都是用于浅拷贝,但这个方法的用处远不止浅拷贝。因为用的比较多就详细介绍一下吧。
(1). 浅拷贝
var Obj = function(name){
this.name = name;
}
var newObj = new Obj('张三');
var copy = Object.assign({},newObj);
console.log(copy);//{name: "张三"}
也许你会好奇:这个为什么叫浅拷贝,跟深拷贝有什么区别呢?那么让我用下面的例子为你解释一下吧!
var Obj = function(name){
this.name = name;
var age = 24;
this.getAge = function(){
return age;
}
}
Obj.prototype.getName = function(){
return this.name;
}
var newObj = new Obj('张三');
var copy = Object.assign({},newObj);
console.log(newObj);//{name: "张三", getAge: ƒ},你点开__proto__是能找到getName的方法的
console.log(copy);//{name: "张三", getAge: ƒ}你点开__proto__是找不到getName的方法的
小结:浅拷贝只是将对象中的属性以及方法进行拷贝,而对象原型上的属性以及方法是没有进行任何拷贝的。
(2). 合并对象:
var Name = {
name:'张三'
}
var Age = {
age:24
}
var Sex = {
sex:'男'
}
var newObj = Object.assign({},Name,Age,Sex);
console.log(newObj);//{name: "张三", age: 24, sex: "男"}
(3). 合并具有相同属性的对象:(后者覆盖前者)
var Name = {
name:'张三'
}
var Age = {
name:'李四',
age:24
}
var Sex = {
name:'王五',
age:25,
sex:'男'
}
var newObj = Object.assign({},Name,Age,Sex);
console.log(newObj);//{name: "王五", age: 25, sex: "男"}
(4). 拷贝访问器:
var Obj = {
name:'张三',
get age(){
return 25
}
}
var copy = Object.assign({},Obj);
console.log(copy);//{name: "张三", age: 25}
- Object.create():创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
var Obj = {
name:'张三',
isShow:false,
getName:function(){
if(this.isShow){
console.log('我的名字叫' + this.name);
}
}
}
var newObj = Object.create(Obj);
newObj.isShow = true;
newObj.getName();//我的名字叫张三
- Object.defineProperties():直接在一个对象上定义新的属性或修改现有属性,并返回该对象。
语法:Object.defineProperties(obj, props)
其中props:
(1). configurable:能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性
(2). enumerable:表示能否通过for-in循环返回属性
(3). value:与属性关联的值。可以是任何有效的JavaScript值(数字,对象,函数等)。
(4). writable:表示能否修改属性值
(5). get:获取值。
(6). set:设置值。
var Obj = {sex:'男'};
Object.defineProperties(Obj, {
'name':{
configurable:true,
enumerable:true,
value:'张三',
writable:true
},
Sex:{
get:function(){
return this.sex;
},
set:function(data){
this.sex = data;
}
}
});
Obj.Sex = '女';
console.log(Obj.sex);//女
- Object.defineProperty:
对象中的数据属性行为特性:
注:修改以下4中数据属性行为的方法:Object.defineProperty
(1). configurable:能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性
var obj = {
age:'24',
sex:'男'
};
Object.defineProperty(obj,'name',{
configurable:true,//规定该属性值是否能被删除,如果为true则能被删除,如果为false则不能被删除
enumerable:false,
//规定该属性是否可以for-in遍历,true则能通过for-in打印出name的值,如果为false则打印不出来name的值
writable:true,//如果为true,第二个name打印为李四,如果为false第二个name打印为张三
value:'张三'//设置name属性的属性值
})
delete obj.name;
console.log(obj);
(2). enumerable:表示能否通过for-in循环返回属性
var obj = {
age:'24',
sex:'男'
};
Object.defineProperty(obj,'name',{
enumerable:false,
//规定该属性是否可以for-in遍历,true则能通过for-in打印出name的值,如果为false则打印不出来name的值
writable:true,//如果为true,第二个name打印为李四,如果为false第二个name打印为张三
value:'张三'
})
for(var key in obj){
console.log(obj[key]);
}
console.log(obj);
(3). writable:表示能否修改属性值
var obj = {};
Object.defineProperty(obj,'name',{
writable:true,//如果为true,第二个name打印为李四,如果为false第二个name打印为张三
value:'张三'
})
console.log(obj.name);
obj.name = '李四';
console.log(obj.name);
(4). value:包含这个属性的数据值
var obj = {};
Object.defineProperty(obj,'name',{
value:'张三'
})
console.log(obj.name);//张三
访问器属性:
注:前两种方法跟对象中的数据属性行为特性中的一样,就不多赘述了
(1). configurable:规定该属性值是否能被删除
(2). enumerable:规定该属性是否可以for-in遍历
(3). get:读取属性时调用的函数
(4). set:写入属性时调用的函数
var obj = {
_name:'李四'
};
Object.defineProperty(obj,'name',{
get:function() {
return this._name;
},
set:function(newName){
if(newName){
this._name = newName;
}
}
})
obj.name = '';
console.log(obj.name);//李四
- Object.entries():返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for...in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环还会枚举原型链中的属性)。
var Obj = {
name:'张三',
id:1
};
//for(var [key,value] of Object.entries(Obj)){
//console.log(key + ',' + value);
//}//name,张三 id,1
console.log(Object.entries(Obj))//[[name,张三],[id,1]];
注:此处用for-in会打印出对象的key值为0和1,然而value打印出来却是未定义。
- Object.values()方法返回一个给定对象自身的所有可枚举属性值的数组,值的顺序与使用for...in循环的顺序相同 ( 区别在于 for-in 循环枚举原型链中的属性 )。
var Obj = {
name:'张三',
id:1
};
//for(var value of Object.values(Obj)){
//console.log(value);
//}//张三 1
console.log(Object.values(Obj));//张三 1
- Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和使用 for...in 循环遍历该对象时返回的顺序一致 。
var Obj = {
name:'张三',
id:1
};
//for(var key of Object.keys(Obj)){
//console.log(key);
//}//name id
console.log(Object.keys(Obj));//name id
- Object.freeze():可以冻结一个对象。
var Obj = {
name:'张三',
id:1
}
Object.freeze(Obj);
Obj.name = '李四';
Obj.sex = '女';
console.log(Obj);//{ name: "张三", id: 1 }
注:该方法可让对象变成只可读不可写的类型,可以有效地防止别人在不需要修改的对象上添加属性。
11. Object.fromEntries() 方法把键值对列表转换为一个对象。
var arr = [['name','张三'],['id',1]];
console.log(Object.fromEntries(arr));//{ name: "张三", id: 1 }
- Object.getOwnPropertyDescriptor() 方法返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)
var Obj = {
name:'张三'
};
console.log(Object.getOwnPropertyDescriptor(Obj,'name'));
//{ value: "张三", writable: true, enumerable: true, configurable: true }
- Object.getOwnPropertyDescriptors() 方法用来获取一个对象的所有自身属性的描述符。
var Obj = {
name:'张三',
id:1
}
console.log(Object.getOwnPropertyDescriptors(Obj));
//name: {value: "张三", writable: true, enumerable: true, configurable: true}
//id: {value: 1, writable: true, enumerable: true, configurable: true}
- Object.getOwnPropertyNames()方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组。
var Obj = {
name:'张三',
id:1
}
console.log(Object.getOwnPropertyNames(Obj));
- Object.getOwnPropertySymbols() 方法返回一个给定对象自身的所有 Symbol 属性的数组。
var obj = {};
var a = Symbol("a");
var b = Symbol.for("b");
obj[a] = "akdfj";
obj[b] = "asdkflj";
var newObj = Object.getOwnPropertySymbols(obj);
console.log(newObj.length); // 2
console.log(newObj) // [Symbol(a), Symbol(b)]
console.log(newObj[0]) // Symbol(a)
- Object.getPrototypeOf() 方法返回指定对象的原型(内部[[Prototype]]属性的值)。
var Obj = {};
console.log(Object.getPrototypeOf(Obj));
/**{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}**/
- Object.is() 方法判断两个值是否是相同的值。
console.log(Object.is([],[]));//false
console.log(Object.is({},{}));//false
console.log(Object.is(NaN,NaN));//true
console.log(Object.is(NaN,0/0));//true
console.log(Object.is("",false)); //false
注:只有左右两侧运行的值与其指向也完全相同时才会返回true
- Object.isExtensible() 方法判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)。
var Obj = {
name:'张三',
id:1
}
Object.freeze(Obj);
console.log(Object.isExtensible(Obj));//false
- Object.isFrozen()方法判断一个对象是否被冻结。
var Obj = {
name:'张三',
id:1
}
Object.freeze(Obj);
console.log(Object.isFrozen(Obj));//true
- Object.isSealed() 方法判断一个对象是否被密封。
(1). 空对象变成密封对象:
var Obj = {};
Object.preventExtensions(Obj);//让空对象变成不可扩展对象
console.log(Object.isSealed(Obj));//true;
(2).非空对象变成密封对象:
var Obj = {
name:'张三',
}
Object.preventExtensions(Obj);//让对象变成不可扩展对象
Object.defineProperty(Obj, "name", { configurable: false });//如果是非空对象必须将自身属性变成不可配置才能成为一个密封对象
console.log(Object.isSealed(Obj));
- Object.preventExtensions()方法让一个对象变的不可扩展,也就是永远不能再添加新的属性。
var Obj = {
name:'张三'
}
Object.preventExtensions(Obj);
console.log(Object.isExtensible(Obj));//false
- hasOwnProperty() 方法会返回一个布尔值,判断对象是否有某个key值。
var Obj = {
name:'张三'
}
console.log(Obj.hasOwnProperty('name'));//true
- isPrototypeOf() 方法用于测试一个对象是否存在于另一个对象的原型链上。
function Bar() {}
function Baz() {}
Baz.prototype = Object.create(Bar.prototype);
var baz = new Baz();
console.log(Bar.prototype.isPrototypeOf(baz));
- propertyIsEnumerable() 方法返回一个布尔值,表示指定的属性是否可枚举。
var Obj = {
name:'张三'
}
console.log(Obj.propertyIsEnumerable('name'));//true
- toLocaleString() 方法返回一个该对象的字符串表示。此方法被用于派生对象为了特定语言环境的目的(locale-specific purposes)而重载使用。
var Obj = {
name:'张三'
}
console.log(Obj.toLocaleString());//[object Object]
注:此方法的返回结果不太懂。
- toString() 方法返回一个表示该对象的字符串。
var Obj = {
name:'张三'
}
console.log(Obj.toString());//[object Object]
注:JSON.stringify(Obj)可将对象转化成json字符串
- valueOf() 方法返回指定对象的原始值。
//Number
var num = 1;
console.log(num.valueOf());//1
//String
var str = '我';
console.log(str.valueOf());//我
//Boolean
var bool = false;
console.log(bool.valueOf());//false
//以此类推
- Object.seal()将对象设置为封闭对象:
var Obj = {
name:'张三'
}
Object.seal(Obj);
console.log(Object.isSealed(Obj));//true
思考:
如何遍历对象原型上的属性及方法名?
var obj = function(){};
obj.prototype.name = '张三';
obj.prototype.id = 1;
obj.prototype.getName = function(){
return this.name;
}
var newObj = new obj;
console.log(newObj);
for(var key in newObj){
console.log(key);
}//name,id.getName
如何实现数据的双向绑定?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>双向绑定</title>
</head>
<body>
<h1 id="title">hello</h1>
<input type="text" id="inp">
<script>
var input = document.getElementById('inp');
var title = document.getElementById('title');
input.oninput = function(e){
title.innerHTML = e.target.value || this.value;
}
//监听input及title值的变化
function monitor(Obj,Objkey){
Object.defineProperty(Obj,Objkey,{
get:function(){
return input.value;
},
set(val){
title.innerHTML = val;
input.value = val;
}
})
}
monitor(title,'inner');
title.inner = '123';
// monitor(input,'val');
// input.val = '456';
</script>
</body>
</html>
如何实现对象转数组?
var Obj = {
name:'张三',
id:1
}
function toArr(Obj){
return Object.entries(Obj).flat();
}
console.log(toArr(Obj));//["name", "张三", "id", 1];
参考文献:
- developer.mozilla.org/zh-CN/docs/…
- javascript高级程序设计(第三版)——尼古拉斯.泽卡斯
后记:
作者始终坚信实践是检验真理的唯一标准,不管是报错也好还是这些零散的知识点也好,所有的代码都只有你自己试过才知道有没有效,没有试过的代码靠猜是猜不出来的,代码的对错浏览器会告诉你答案的!另外以上的知识点很多概念都是我自己理解了之后用自己的话表述的,如果有说错的地方,还望各位大佬指点一二。