你不知道的es5的面向对象

前言:

本文只为本身最近看了不少关于es5对象方面的视频以及书籍,深感前端之路路漫漫其修远兮,才写了此篇博客,也是鄙人第一次写博客,写的很差你们勿怪。另外本文总结的是es5方面的知识点,就不会用到什么let,const以及class之类的es6语法,为了与标题保持一致。我也会在以后总结es6以及typescript方面的知识。还有本文的全部代码都是亲测运行有效后才复制上来的,基本不会存在报错的状况,若是有错误或者代码哪儿有问题还望各位大佬多多指教,毕竟我也只是正在前端学习的路上而已,也不是什么大牛。代码并不是是书上的源码,我写的比较简单,缘由是照顾一些初学者。若是你在做者的文章中学到了一些新知识不仿给做者点个赞吧,在这里先谢谢了。javascript

对象的定义:

无序属性的集合(简单易懂一点,不喜欢扯太复杂,也不喜欢拖泥带水)html

重要概念:

  1. 私有变量:对于对象而言,全部的属性都是公有的,严格来说是没有私有属性的说法的,但变量是有私有的。
  2. 私有方法:在构造函数内部不能为外界访问,只能经过函数内部方法访问的方法。
  3. 公有属性:在构造函数内部,能够为外界直接访问的属性。
  4. 公有方法:在构造函数内部,能够为外界直接访问的方法。
  5. 特权方法:能够访问构造函数中私有属性的方法。
  6. 静态属性和静态方法:无需实例化就能够调用的属性和方法就叫静态属性和静态方法。
  7. 实例:程序使用类建立对象时,生成的对象叫做类的实例
  8. 实例化:由类建立对象实例的过程叫作实例化
  9. 原型链:当访问某个对象的属性时,若是该对象没有此属性,则会到他的原型中去查找,它的原型属性中又有本身的原型属性,一直这样查找下去,这就构成了原型链(仅表明我的理解,并未找到参考资料)
  10. 原型:对象中固有的__proto__属性,该属性指向对象的prototype原型属性。
  11. this指向:正常来说this指向其执行上下文环境,但this的指向也不是固定不变的,后面会深刻讨论this的指向问题。
  12. 闭包:是可访问上一层函数做用域里变量的函数,即使上一层函数已经关闭。
  13. 递归:递归最简单的解释就是本身调用本身
  14. 做用域:window中声明的变量是全局变量,全局变量处在全局做用域中,函数中声明的变量是局部变量,局部变量在函数做用域中。(另外在es6用let和const在代码块中声明的变量具备块级做用域)
  15. 做用域链:函数访问某一个变量值时,此函数会在该函数中寻找该变量,若是没有则到函数外寻找,最终会到window中寻找,这就造成了做用域链。
  16. 浅拷贝:只复制一层对象属性,只是将数据中存放的引用拷贝下来,但依旧指向同一个存放地址。
  17. 深拷贝:则递归复制了全部层级,将数据中全部的数据都拷贝下来,而不只仅是引用。拷贝下来的数据的修改,并不会影响原数据。

对象的声明方式:

  1. 字面量方式声明:
    优缺点:简单快捷,使用一个接口建立对象会产生大量重复代码
var obj = {};
复制代码
  1. 构造函数方式声明:
    优缺点:能够标识对象的类型,但每一个方法都会在实例上从新建立一遍
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);
复制代码
  1. 工厂模式声明:
    优缺点:能够建立多个类似对象,但未解决对象类型的识别问题
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);
复制代码
  1. 原型模式声明:
    优缺点:它的优势不只能自定义对象类型,更重要的是能够在全部引用类型(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 : '男'
	}
复制代码
  1. 动态原型模式:
    优缺点:集合了构造函数和原型的优势,但不能使用对象字面量重写原型
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();
复制代码
  1. 寄生构造函数模式:
    优缺点:优势太过奇特了,有点儿说不上来,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());
复制代码
  1. 稳妥的构造函数模式:
    优缺点:没有任何除了定义的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();
复制代码
  1. 混合模式:
    注:以构造函数和原型模式组合为例。
    优缺点:能够结合其余模式的优势,缺点目前没发现
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();//李四
复制代码

对象的特性:

前言:众所周知对象有三个特性,也是比较重要的特性——封装、继承、多态。java

封装:封装就是有些私有的属性和方法,用户只能经过公有方法去访问这些私有属性。

注:文档开头已注明了基本概念es6

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);//多态指得就是调用同一个函数产生不一样对象的这种特性
复制代码

继承:一个对象继承另外一个对象的属性以及方法(最重要)

  1. 原型链方式继承:
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());
复制代码
  1. 构造函数方式继承:
var Obj = function(name){
    this.name = name;
};
var newObj = function(){
    Obj.call(this,'张三');
};
var O = new newObj();
console.log(O.name);//张三
复制代码
  1. 组合方式继承:(原型链+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());//张三
复制代码
  1. 寄生继承:
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());//张三
复制代码
  1. 寄生组合式继承:
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());//张三
复制代码
  1. 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());//张三
复制代码
  1. 对象冒充:
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());
复制代码
  1. 原型式继承:
var inherit = function(o){//实现继承的函数
    function F(){};
    F.prototype = o;
    return new F();
}
var Obj = {
    name:'张三'
}
var newObj = inherit(Obj);
console.log(newObj.name);//张三
复制代码

this指向问题:

  1. 在js文件中直接打印this,打印结果为window:
console.log(this);//window
复制代码
  1. 在函数中打印this,打印结果为Window:
function fn(){
        console.log(this);//Window
    };
    fn();
复制代码
  1. 在对象中打印this,this的指向为该对象:
var Obj = {
		_this : function(){
			return this;
		}
    }
	console.log(Obj._this());//Obj
复制代码
  1. 若是是绑定的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的指向呢?

  1. 经过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: ƒ}
复制代码
  1. 经过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: ƒ}
复制代码
  1. 经过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中有全局做用域和函数做用域。
全局做用域:在函数做用域中,能访问到全局做用域中的变量typescript

var num = 1;
    function fn(){
        console.log(num);//1
    }
    fn();
复制代码

函数做用域:在函数中的变量,在函数外部的全局做用域中是访问不到的json

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变量了。
小结:若是要经过一个标准的方法访问函数中的变量的话,推荐使用闭包。

闭包:

  1. 基本概念:是可访问上一层函数做用域里变量的函数,即使上一层函数已经关闭。(我的理解:能够访问函数中局部变量的函数叫作闭包)
  2. 使用场景:当咱们须要在模块中定义一些变量,并但愿这些变量一直保存在内存中但又不会 “污染” 全局的变量时,就能够用闭包来定义这个模块。
  3. 代码示例:
function fn(){
        var name = '张三';
        var age = 20;
        return function(){
            return name + '今年' + age + '岁了';
        }
    }
    var newFn = fn();
    console.log(newFn());//张三今年20岁了
复制代码
  1. 闭包的优缺点:
    优势:1:变量长期驻扎在内存中;2:避免全局变量的污染;3:私有成员的存在 ;
    缺点:1.致使内存泄漏;2.会改变父函数内部变量的值
    产生内存泄漏的缘由: 若是一个对象再也不被引用,那么这个对象就会被 GC回收,不然这个对象一直会保存在内存中(两个对象相互引用也会被回收,另外提一句谷歌v8引擎下是分代式回收机制:新生代和老生代)
  2. 解决闭包内存泄漏的方法:在退出函数以前,将不使用的局部变量所有删除。
  3. 垃圾回收策略:标记清除(较为经常使用)和引用计数。

递归:

  1. 基本概念:函数调用自身的函数名形成的
  2. 代码示例:
function fn(n){
		if(n <= 1){
			return 1;
		}else{
			return n*fn(n-1);
		}
	}
	console.log(fn(10));//3628800
复制代码

3.递归的优缺点:
优势:在树的前序,中序,后序遍历算法中,递归的实现明显要比循环简单得多。
缺点:

  1. 递归因为是函数调用自身,而函数调用是有时间和空间的消耗的:每一次函数调用,都须要在内存栈中分配空间以保存参数、返回地址以及临时变量,而往栈中压入数据和弹出数据都须要时间。->效率
  2. 递归中不少计算都是重复的,因为其本质是把一个问题分解成两个或者多个小问题,多个小问题存在相互重叠的部分,则存在重复计算,如fibonacci斐波那契数列的递归实现。->效率
  3. 调用栈可能会溢出,其实每一次函数调用会在内存栈中分配空间,而每一个进程的栈的容量是有限的,当调用的层次太多时,就会超出栈的容量,从而致使栈溢出。->性能

深浅拷贝:

  1. 浅拷贝:
    基本概念:只复制一层对象属性,只是将数据中存放的引用拷贝下来,但依旧指向同一个存放地址。
    实现方式:
    (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
复制代码
  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的属性与方法列举:(力求作到简单易懂)

前言:若是有没有列举到的属性以及方法欢迎补充
先看一下基本目录吧:
(这是官方解释,后面我会根据本身的理解给你们简单的解释一下)

属性:

  1. Object.prototype:属性表示 Object 的原型对象
  2. Object.prototype.constructor:全部对象都会从它的原型上继承一个 constructor 属性

方法:

  1. Object.assign():用于将全部可枚举属性的值从一个或多个源对象复制到目标对象。
  2. Object.create():建立一个新对象,使用现有的对象来提供新建立的对象的__proto__。
  3. Object.defineProperties():直接在一个对象上定义新的属性或修改现有属性,并返回该对象。
  4. Object.defineProperty():会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
  5. Object.entries():返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for...in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环还会枚举原型链中的属性)。
  6. Object.values()方法返回一个给定对象自身的全部可枚举属性值的数组,值的顺序与使用for...in循环的顺序相同 ( 区别在于 for-in 循环枚举原型链中的属性 )。
  7. Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和使用 for...in 循环遍历该对象时返回的顺序一致 。
  8. Object.freeze():能够冻结一个对象。
  9. Object.fromEntries() 方法把键值对列表转换为一个对象。
  10. Object.getOwnPropertyDescriptor() 方法返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不须要从原型链上进行查找的属性)
  11. Object.getOwnPropertyDescriptors() 方法用来获取一个对象的全部自身属性的描述符。
  12. Object.getOwnPropertyNames()方法返回一个由指定对象的全部自身属性的属性名(包括不可枚举属性但不包括Symbol值做为名称的属性)组成的数组。
  13. Object.getOwnPropertySymbols() 方法返回一个给定对象自身的全部 Symbol 属性的数组。
  14. Object.getPrototypeOf() 方法返回指定对象的原型(内部[[Prototype]]属性的值)。
  15. Object.is() 方法判断两个值是不是相同的值。
  16. Object.isExtensible() 方法判断一个对象是不是可扩展的(是否能够在它上面添加新的属性)。
  17. Object.isFrozen()方法判断一个对象是否被冻结。
  18. Object.isSealed() 方法判断一个对象是否被密封。
  19. Object.preventExtensions()方法让一个对象变的不可扩展,也就是永远不能再添加新的属性
  20. hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具备指定的属性(也就是,是否有指定的键)。
  21. isPrototypeOf() 方法用于测试一个对象是否存在于另外一个对象的原型链上。
  22. propertyIsEnumerable() 方法返回一个布尔值,表示指定的属性是否可枚举。
  23. toLocaleString() 方法返回一个该对象的字符串表示。此方法被用于派生对象为了特定语言环境的目的(locale-specific purposes)而重载使用。
  24. toString() 方法返回一个表示该对象的字符串。
  25. valueOf() 方法返回指定对象的原始值。
  26. Object.seal()方法封闭一个对象,阻止添加新属性并将全部现有属性标记为不可配置。当前属性的值只要原来是可写的就能够改变。

具体解析:

  1. 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岁
复制代码
  1. Object.prototype.constructor:全部对象都会从它的原型上继承一个 constructor 属性
var Obj = function(name){
    	this.name = name; 
    }
    var newObj = new Obj('张三');
    console.log(newObj.constructor);//ƒ (name){this.name = name; }
复制代码
  1. 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}
复制代码
  1. 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();//个人名字叫张三
复制代码
  1. 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);//女
复制代码
  1. 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);//李四
复制代码
  1. 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打印出来倒是未定义。

  1. 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
复制代码
  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
复制代码
  1. 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 }
复制代码
  1. Object.getOwnPropertyDescriptor() 方法返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不须要从原型链上进行查找的属性)
var Obj = {
    name:'张三'
};
console.log(Object.getOwnPropertyDescriptor(Obj,'name'));
//{ value: "张三", writable: true, enumerable: true, configurable: true }
复制代码
  1. 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}
复制代码
  1. Object.getOwnPropertyNames()方法返回一个由指定对象的全部自身属性的属性名(包括不可枚举属性但不包括Symbol值做为名称的属性)组成的数组。
var Obj = {
    name:'张三',
    id:1
}
console.log(Object.getOwnPropertyNames(Obj));
复制代码
  1. 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)
复制代码
  1. Object.getPrototypeOf() 方法返回指定对象的原型(内部[[Prototype]]属性的值)。
var Obj = {};
console.log(Object.getPrototypeOf(Obj));
/**{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}**/
复制代码
  1. 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

  1. Object.isExtensible() 方法判断一个对象是不是可扩展的(是否能够在它上面添加新的属性)。
var Obj = {
	    name:'张三',
	    id:1
	}
	Object.freeze(Obj);
	console.log(Object.isExtensible(Obj));//false
复制代码
  1. Object.isFrozen()方法判断一个对象是否被冻结。
var Obj = {
	    name:'张三',
	    id:1
	}
	Object.freeze(Obj);
	console.log(Object.isFrozen(Obj));//true
复制代码
  1. 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));
复制代码
  1. Object.preventExtensions()方法让一个对象变的不可扩展,也就是永远不能再添加新的属性。
var Obj = {
    name:'张三'
}
Object.preventExtensions(Obj);
console.log(Object.isExtensible(Obj));//false
复制代码
  1. hasOwnProperty() 方法会返回一个布尔值,判断对象是否有某个key值。
var Obj = {
    name:'张三'
}
console.log(Obj.hasOwnProperty('name'));//true
复制代码
  1. isPrototypeOf() 方法用于测试一个对象是否存在于另外一个对象的原型链上。
function Bar() {}
function Baz() {}
Baz.prototype = Object.create(Bar.prototype);
var baz = new Baz();
console.log(Bar.prototype.isPrototypeOf(baz)); 
复制代码
  1. propertyIsEnumerable() 方法返回一个布尔值,表示指定的属性是否可枚举。
var Obj = {
    name:'张三'
}
console.log(Obj.propertyIsEnumerable('name'));//true
复制代码
  1. toLocaleString() 方法返回一个该对象的字符串表示。此方法被用于派生对象为了特定语言环境的目的(locale-specific purposes)而重载使用。
var Obj = {
    name:'张三'
}
console.log(Obj.toLocaleString());//[object Object]
复制代码

注:此方法的返回结果不太懂。

  1. toString() 方法返回一个表示该对象的字符串。
var Obj = {
    name:'张三'
}
console.log(Obj.toString());//[object Object]
复制代码

注:JSON.stringify(Obj)可将对象转化成json字符串

  1. 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
//以此类推
复制代码
  1. 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];
复制代码

参考文献:

  1. developer.mozilla.org/zh-CN/docs/…
  2. javascript高级程序设计(第三版)——尼古拉斯.泽卡斯

后记:

做者始终坚信实践是检验真理的惟一标准,不论是报错也好仍是这些零散的知识点也好,全部的代码都只有你本身试过才知道有没有效,没有试过的代码靠猜是猜不出来的,代码的对错浏览器会告诉你答案的!另外以上的知识点不少概念都是我本身理解了以后用本身的话表述的,若是有说错的地方,还望各位大佬指点一二。

相关文章
相关标签/搜索