一、Object对象
1.1、什么是对象
对象是由一对或者多对属性(包含属性名和属性值)组成的集合,属性名也叫键名,属性值也叫键值,所以对象的属性也可以称为键值对,对象就是属性的无序集合
1.2、对象的属性
对象有属性,所谓的属性就是这个对象的特点、特性
在特殊情况下,属性是必须加引号的
属性为特殊字符
属性为数字
属性中有空格
关键字、保留字
特殊形式的属性,必须要加上引号,检索属性的时候,必须要用方括号[‘name’],如果属性名符合标识符规范,可以加引号,也可以不加引号,如果不符合标识符规范,则属性必须加引号
对象的属性访问:
点语法:只能访问符合标识符规范的属性
中括号:要加引号,不加引号就是变量
1.3、对象的方法
对象只有属性,但是如果这个属性的值是一个函数,那么这个函数我们就称为对象的”方法“,比如下面的案例,sayHello就是一个属性,只不过它的值是一个函数,所有我们说obj这个对象,有sayHello方法
var obj={
name:'小明',
age:19,
sayHello:function(){
console.log('大家好,我叫'+this.name);
}
}
console.log(obj.name);
console.log(obj.age);
console.log(obj.sayHello());
1.4对象的遍历
var obj={
name:'小明',
age:19,
sayHello:function(){
console.log('大家好,我叫'+this.name);
}
}
for(var k in obj){
console.log(k);
console.log(obj.k);
}
二:对象的创建
2.1、字面量方式
var obj={
name:'小明',
age:19
}
console.log(obj);
2.2、通过new关键字创建对象
var obj=new Object(); //创建的是一个空对象,要往里面添加属性和值
obj.name='小红';
obj.age=20;
console.log(obj);
2.3、对象的常用操作
var obj={
name:'小明',
age:19,
sayHello:function(){
console.log('大家好,我叫'+this.name);
}
}
obj.name='小红'; //改
obj.sex='女'; //增
delete obj.age; //删
console.log(obj.name); //查
console.log(obj);
2.4、保护对象
Object.preventExtensions()
阻止扩展,可以修改,但是不可以删除
Object.seal()
密封,不能增删
Object.freeze()
冻结,不能增删改
ES6新增的对象
(1)Object.is()
用来比较两个值是否相等
Object.is(NaN,NaN)
//true
NaN==NaN
//false
Object.is(+0,-0)
//false
+0==-0
//true
Object.is({},{})
//false
{}=={}
//false
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iLA3KF7e-1635345187181)(C:\Users\web\AppData\Roaming\Typora\typora-user-images\image-20210930141847982.png)]
(2)Object.assign()
用来合并对象的
用法:let 新对象=Object.assign(目标对象,需要合并的对象);
let x={a:1};
let y={b:2};
let z={c:3};
let obj=Object.assign({},x,y,z);
console.log(obj);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TN8eVQZo-1635345187184)(C:\Users\web\AppData\Roaming\Typora\typora-user-images\image-20210930143450665.png)]
如果合并的对象的属性名有重复就是后面的会覆盖前面的
let x={a:1};
let y={b:2,a:4};
let z={c:3};
let obj=Object.assign({},x,y,z);
console.log(obj);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NdmHmsiy-1635345187190)(C:\Users\web\AppData\Roaming\Typora\typora-user-images\image-20210930143800622.png)]
如果只有一个参数,Object.assign
会直接返回该参数。
let x={a:1};
let obj=Object.assign(x);
console.log(obj);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UDHxlMdk-1635345187192)(C:\Users\web\AppData\Roaming\Typora\typora-user-images\image-20210930144123070.png)]
由于undefined
和null
无法转成对象,所以如果它们作为参数,就会报错。
Object.assign(undefined) // 报错
Object.assign(null) // 报错
可以利用这个方法来拷贝数组
//数组中的arr.push()方法会改变原数组
let x=[1,2,3];
let arr=Object.assign([],x);
console.log(arr);
arr.push(4);
console.log(arr);
console.log(x);
(3)Object.keys()、 Object.entries() 、Object.values()
let obj={
name: 'zhangsan',
age: 18,
sex: '男'
}
console.log(obj);
//遍历属性
for(let key of Object.keys(obj)){
console.log(key);
}
//遍历属性值
for(let val of Object.values(obj)){
console.log(val);
}
//解构遍历的键值对
for(let [val,key] of Object.entries(obj)){
console.log(val,key);
}
//遍历键值对
for(let item of Object.entries(obj)){
console.log(item);
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lPXYfk5u-1635345187193)(C:\Users\web\AppData\Roaming\Typora\typora-user-images\image-20210930150230824.png)]
(4)对象的解构赋值与…运算符
let {x,y,...z}={x:1,y:2,z:3,t:4};
console.log(x,y,z);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cH7axFFZ-1635345187194)(C:\Users\web\AppData\Roaming\Typora\typora-user-images\image-20210930152418557.png)]
由于解构赋值要求等号右边是一个对象,所以如果等号右边是undefind或者null,就会报错,因为它们无法转为对象
解构赋值必须是最后一个参数,否则会报错
解构赋值的拷贝是浅拷贝,即如果是一个键的值是复合类型的值(数组,对象,函数),那么解构赋值拷贝的是这个值的引用,而不是这个值的副本
扩展运算符(…)用于取出参数对象的所有遍历属性,拷贝到当前对象中,这等同于使用Object.assign方法
三、构造函数
3.1、什么是构造函数
用new关键字来调用的函数,称为构造函数,构造函数首字母一般大写,一个构造函数创造的对象被称为该构造函数的实例
常见的内置构造函数
Object()
Array();
RegExp();
Function();
Date();
3.2、构造函数的原理
一个函数可以用new关键字来调用,那么会按顺序发生四件事
1.隐秘的创建一个新的空对象
2.将这个函数里面的this绑定到刚才创建的隐秘新对象上
3.执行函数体内的语句
4.返回这个新的对象
function Fn(name,age){
/*
1.隐秘的创建一个this
2.将这个函数里面的this绑定到刚才创建的隐秘新对象上
this:{
name:undefind '小明'
age:undefind 19
}
*/
this.name=name; //执行代码
this.age=age;
//4.return这个this,赋给xiaoming
}
//new关键字来调用函数,那么函数就叫做构造函数,将返回一个对象
//new关键字造出来的xiaoming,我们称为Fn类的实例,可以理解为实体
var xiaoming=new Fn('小明',19); //Fn函数就是构造函数 xiaoming这个对象是Fn类的实例
console.log(xiaoming);
3.3、构造函数类和实例
当一个函数被new关键字来调用,这个函数称为构造函数
函数里面的语句会被执行,类是具有相同属性和方法的集合
function People(name,age){
/*
AO{
this:{
name:小明
age:18
sayHello:f(){}
}
name:undefind 小明
age:undefind 18
sayHello:f(){}
}
*/
this.name=name; //第一个name是this里面的,第二个name是函数作用域里面的,先找函数作用域有没有name值,有就赋值给this里面的name
this.age=age;
this.sayHello=function(){
console.log('大家好,我叫'+this.name);
}
}
/*
xiaoming{
name:小明
age:18
sayHello:f(){}
}
*/
var xiaoming=new People('小明',18); //People是构造函数,也叫做People类,xiaoming是对象,xiaoming这个对象是People类的实例
var xiaohong=new People('小红',17);
console.log(xiaoming); //People {name: "小明", age: 18, sayHello: ƒ}
xiaoming.sayHello(); //大家好,我叫小明
console.log(xiaohong); //People {name: "小红", age: 17, sayHello: ƒ}
xiaohong.sayHello(); //大家好,我叫小红
function People(){
this.name='张三';
this.age=19;
this.sayHello=function(){
console.log(this.name);
}
}
// 使用new关键字来调用函数,那么函数就叫做构造函数,将返回一个对象
var zhangsan=new People();
console.log(zhangsan); //People {name: "张三", age: 19, sayHello: ƒ}
3.4、构造函数的注意事项
1、如果函数里面没有this,那么将创建一个空对象
/*构造函数里面如果没有this,那么就废了,就不能给隐秘新创建的对象添加属性,但是里面的语句还是能执行,所以new出来的是一个空对象*/
function People(){
for(var i=1,sum=0;i<=100;i++){
sum+=i;
}
console.log(sum); //5050
}
var xiaoming=new People();
console.log(xiaoming); //People{}
2.构造函数中,不允许出现return语句
构造函数总不能出现return,严格来说不能return引用类型数据,如果出现return语句将返回引用类型值,那么构造函数将不能返回新创建的那个对象,而是返回return语句后面的内容
function People(name,age){
this.name=name;
this.age=age;
// return undefined; //Peopel{name:'小明',age:19}
// return null; //Peopel{name:'小明',age:19}
// return []; //[]
// return {a:1,b:2}; // {a:1,b:2}
return 1; //Peopel{name:'小明',age:19}
}
var xiaoming=new People('小明',19);
console.log(xiaoming);
console.log(xiaoming.name);
3.5、总结
- 当一个**函数()**调用的时候,this就是window
- 当一个函数用**对象.方法()**调用的时候,this就是这个对象
- 当一个函数绑定给一个HTML元素事件的时候,this就是这个HTML元素
- 当一个函数用定时器调用的时候,this就是window
- 当一个函数用apply、call调用的时候,this就是你指定的第一个参数
- 当一个函数用new调用的时候,this就是隐秘创建的空对象,函数里面的语句将被执行,并且返回新对象
四、原型prototype
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LTSuGBpQ-1635345187194)(C:\Users\web\AppData\Roaming\Typora\typora-user-images\image-20210903102405933.png)]
4.1、原型prototype
原型是构造函数创建对象的原始模型
原型的特点
原型也是对象,是函数对象的一个属性
原型自带constructor属性,constructor指定构造函数
构造函数创建出来的对象会继承原型的属性和方法
4.2、原型对象与实例
当一个对象被new出来的时候,不仅仅执行了构造函数里面的语句,而且在构造函数的原型中,所有的属性也给加了这个对象
prototype一定是函数的属性,但这个函数是一个构造函数的时候,那么它new出来的对象,它的原型对象为new出来的实例的原型对象
// function People(name,age){
// this.name=name;
// this.age=age;
// }
// People.prototype={
// sex:'男',
// waihao:'小白'
// }
// var xiaoming=new People('小明',12);
// var xiaohong=new People('小红',13);
// console.log(xiaoming);
// console.log(xiaoming.name);
// console.log(xiaoming.sex);
// console.log(xiaohong.waihao);
4.3、实例对象的__proto__属性
当一个对象被new出来的时候,不仅执行了构造函数里面的语句,也会把这个函数的__protp__执指向构造函数的prototype
//构造函数里面没有任何的语句,也就是说,这个函数在执行的时候,不会给创建出来的对象添加任何属性
function People(){}
//构造函数的原型,我们更改了构造函数的原型,为一个新的对象
People.prototype={
name:'小明',
age:12,
sex:'男'
}
//当一个对象被new出来的时候,不仅仅执行了构造函数里面的语句,也会把这个函数的__proto__指向构造函数的prototype
console.log(xiaoming.__proto__); //{name: "小明", age: 12, sex: "男"} 属性在原型对象上
//xiaoming这个对象的原型对象===构造函数的原型
console.log(xiaoming.__proto__==People.prototype); //true
var xiaoming=new People();
console.log(xiaoming); //People{} 空对象
//当我们访问name属性的时候,自己身上没有,就会去找原型,原型身上有,就当做自己的属性返回了
console.log(xiaoming.name); //小明 自己没有,找原型对象
当访问一个对象身上属性的时候,如果这个对象身上有这个属性,则返回它的值,如果它的身上没有这个属性,那么将访问它的原型对象,检测它的原型身上是否有这个值,如果有返回它原型对象身上的这个值
任何一个函数都有原型,原型是一个对象。用prototype来访问,当这个函数为构造函数的时候,new出来的对象。它的原型对象就是这个构造函数的原型
prototype称为‘原型’,只有函数才有
__proto__称为‘原型对象’,任何对象都有原型对象
4.4、原型的应用
在原型上储存构造函数创建出对象的共有属性和方法,可以避免代码冗余
//在构造函数里面,负责定义一些属性,随着构造函数的执行,这个属性将绑定到new出来的对象身上
function People(name,age){
this.name=name;
this.age=age;
}
//把所有的方法,定义在原型对象上
// People.prototype={
// sayHello:function(){
// console.log('大家好,我叫'+this.name);
// }
// }
People.prototype.sayHello=function(){
console.log('大家好,我叫'+this.name);
}
var xiaoming=new People('小明',12);
var xiaohong=new People('小红',12);
console.log(xiaoming);
xiaoming.sayHello();
五、创建对象的常见模式
5.1、工厂模式
function People(name,age){
var o=new Object(); //创建空对象
o.name=name;
o.age=age;
o.sayHello=function(){
console.log(this.name);
}
return o;
}
var xiaoming=new People('小明',12);
console.log(xiaoming); //People {name: "小明", age: 12, sayHello: ƒ}
function People(name,age){
var obj={};
obj.name='张三';
obj.age=19;
return obj;
}
var zhangsan=new People();
console.log(zhangsan);
5.2、构造函数模式
function People(name,age){
this.name=name;
this.age=age;
this.sayHello=function(){
console.log(this.name);
}
}
var xiaoming=new People('小明',12);
console.log(xiaoming); //People {name: "小明", age: 12, sayHello: ƒ}
5.3、原型模式
借助函数的原型,将一些实例对象共享的属性和方法放在原型对象中,这样子就不必在构造函数中定义重复的方法
好处:所有的对象实例共享它的属性和方法(即所谓的共有属性)
坏处:省略了构造函数传递初始值参数,导致所有的实例对象都是相同的属性和方法
function People(){}
People.prototype={
name:'小明',
age:19,
sayHello:function(){
console.log(this.name);
}
}
console.log(People.prototype); //{name: "小明", age: 19, sayHello: ƒ} 构造函数的原型(prototype)==对象的原型对象(__proto__)
var xiaoming=new People(); //创建了一个实例xiaoming
console.log(xiaoming.name); //小明
var xiaohong=new People(); //创建了一个实例xiaohong
console.log(xiaohong.__proto__); //{name: "小明", age: 19, sayHello: ƒ} xiaohong.__proto__==People.prototype
xiaohong.name='小红'; //给构造函数添加属性
console.log(xiaohong); //People {name: "小红"}
console.log(xiaohong.name);
5.4、混合模式(构造函数模式+原型模式)
构造函数模式用于定义实例属性,原型模式用于定义方法和共享的属性。
混合模式共享着对相同方法的引用,又保证了每个实例有自己的私有属性,最大限度的节省了内存
function People(name,age){
this.name=name;
this.age=age;
}
People.prototype={
//每个函数都有prototype属性,指向该函数的原型对象,原型对象都有constructor属性,这个是一个指向prototype属性所在函数的指针
constructor:People,
sayHello:function(){
console.log(this.name);
}
}
var xiaoming=new People('小明',12);
var xiaohong=new People('小红',13);
console.log(xiaoming); //People {name: "小明", age: 12}
xiaoming.sayHello();
console.log(xiaohong); //People {name: "小红", age: 13}
xiaohong.sayHello();
六、原型链
6.1、原型链机制
只要是对象,一定有原型对象,只要这个东西是对象,那么一定有__proto__属性
function People(){}
var xiaoming=new People();
console.log(xiaoming); //People {}
console.log(xiaoming.__proto__); //{constructor: ƒ}
console.log(xiaoming.__proto__.constructor); //People(){}
console.log(xiaoming.__proto__.__proto__); //{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
console.log(xiaoming.__proto__.__proto__.constructor); //ƒ Object() { [native code] }
js的世界只有一个对象没有原型对象,这个对象就是Object.prototype
Object是一个函数,是系统内置的构造函数,用于创造对象的,Object.prototype是所有对象的原型链终点
当我们在一个对象上打点调用某个方法的时候,系统会沿着原型链去寻找它的定义,一直找到Object.prototype
//Object.prototype是所有对象的原型链的终点,所以我们直接给Object.prototype增加一个方法,那么世界上所有的对象都能调用这个方法
Object.prototype.sayHello=function(){
console.log('你好');
}
var arr=[1,2,3];
arr.sayHello();
'没没没'.sayHello();
七、对象与属性
7.1、对象直接打点验证某个属性是否存在
对象打点调用属性,可以看出来属性是否在自己身上或者原型链上,如果在,就返回值,如果不存在就返回undefind
var obj={
a:1,
b:2,
c:undefined
}
//只要是对象,就一定有原型对象__proto__
obj.__proto__={
d:3
}
console.log(obj); //{a: 1, b: 2, c: undefined}
console.log(obj.a); //1
console.log(obj.d); //3 在自己身上没有d,就找原型链上有没有d
console.log(obj.c); //undefind 有个误会,比如obj.c的值为undefined,那么obj.c还是返回undefined,不知道c属性是否存在
7.2、in运算符
返回一个布尔值,表示这个属性是不是对象的属性
in不仅仅检测是对象自己有没有这个属性,也会检测原型链上有没有,如果有就返回true,没有就false,in操作符会进行原型链查找
var obj={
a:1,
b:2,
c:false
}
obj.__proto__={
d:20
}
console.log('a' in obj); //true
console.log('d' in obj); //true
//for循环会把原型链上所有的可枚举的属性列出来,不仅把自己身上的属性还有原型链上的所有属性都列出来,但是系统默认的属性(比如constructor)都是不可枚举的
for(var k in obj){
console.log(k);
}
7.3、hasOwnProperty方法
这个方法定义在了Object.prototypr对象上面,所以任何一个Object都能拥有这个方法
这个方法返回true、false。表示自己是否拥有这个属性,不考虑原型链,就看自己身上有没有这个属性,不进行原型链查找
var obj={
a:1,
b:2
}
obj.__proto__={
c:3
}
console.log(obj.hasOwnProperty('a')); //true
console.log(obj.hasOwnProperty('c')); //flase
for(var k in obj){
//短路算法,如果obj.hasOwnProperty(k)为真,则输出后面
obj.hasOwnProperty(k)&&console.log(k); //a,b
}
7.4、定义属性:Object.defineProperty()
js引擎允许对属性操作的控制,需要使用方法Object.defineProperty()来实现,这个方法接三个参数:属性所在的对象、属性的名字、描述符对象
7.5、读取属性的描述对象Object.getOwnPropertyDescriptor()
7.6、instanceof运算符
A instanceof B
验证A对象是不是B类的实例,但是不能证明A是new B()出来,因为可能是继承
function Dog(){}
//实例化一个dog对象
var dog=new Dog();
console.log(dog instanceof Dog); //true
function Dog(){}
function Cat(){}
Cat.prototype=new Dog(); //Cat是父亲,Dog是儿子,儿子继承了父亲
var mao=new Cat(); //通过cat实例一个
console.log(mao);
console.log(mao.constructor); //ƒ Dog(){}
7.7、Object.setPrototypeOf
Object.setPrototypeOf(对象);
创建并指定原型
setPrototypeOf():可以为已存在的
八、继承
function Dog(){}
function Cat(){}
Cat.prototype=new Dog(); //Cat是new Dog创建出来的,Cat是mao的母亲,Dog是mao的父亲
var mao=new Cat();
console.log(mao); //__proto__: Dog __proto__指向的是父亲,Dog是父亲
console.log(mao.constructor); //ƒ Dog(){}
8.1、利用原型对象实现继承
原型对象是JavaScript实现继承的传统方式,如果一个对象中本来没有某个属性或方法,但是可以从另一个对象中获得,就实现了继承
function Person(name){
this.name=name;
}
Person.prototype.sayHello=function(){
console.log(`大家好,我叫${this.name}`);
}
var p1=new Person('jack');
var p2=new Person('lili');
console.log(p1,p2);
p1.sayHello();
p2.sayHello();
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mAgSgUdr-1635345187195)(C:\Users\web\AppData\Roaming\Typora\typora-user-images\image-20210909171857851.png)]
对象 p1、p2 原本没有 sayHello() 成员,但是在构造函数 Person 的原型对象添加了 sayHello() 成员后,p1、p2 也就拥有了 sayHello() 成员,因此,上述代码可以理解为 p1、p2 对象继承了原型对象中的成员