js中的对象也可以看成是一个变量,但这个变量中可以继续包含变量,并把里面的变量称为属性,通常属性和对应属性的值是以键值对(key:value)的形式显示的。此外,对象中可以放入函数,放在对象中的函数也可以叫做方法。js中几乎所有事物都被看成是对象,但注意基本数据类型并非对象。
例如:生活中,每个人就是一个对象,对象有自个儿的属性和属性值,例如一个人的身高(属性名)为175cm(属性值),也有自个的动作行为(方法),例如人说话、跑步。随让每个人可以都有这些属性和方法,但每个人的属性都不尽相同,每个人的动作执行的时间也有差异。并且有的人可能甚至拥有别人没有的属性和动作行为(方法)。
var person = {
name:"张三",
age:30,
gender:'male',
sayName:function(){
console.log("my name is ",this.name);
}
};
分析:person就是一个对象,其内包含了三个属性和一个方法,属性通过
属性名:属性值
的形式表示,方法通过方法名:function(){//具体实现}
的形式表示。整个对象使用"{}“作为对象的边界,属性与属性之间使用逗号’,'隔开,属性名和属性值之间通过”:"隔开,属性名一般不加引号(但当属性名中出现特殊字符的时候需要添加引号),属性值如果是字符串则一定要添加引号(双引号或者单引号)。this.name表示调用该对象中属性名为name的属性。
使用Object或者使用自定义构造函数来初始化对象。这种模式用到的是js核心原型链。
var person = new Object();
//new Object()表示创建一个对象,用变量person来接收,这个person也就成为了对象。
person.name = "terry";
person.age = 12;
person.sayName = function(){
console.log("my name is",this.name);
}
//等价于 <===>
person obj = {};
person.name = "terry";
person.age = 12;
分析:先创建指定的对象,通过
对象名.属性名 = 属性值,
的方式来创建对象的属性并且指定属性值。通过对象名.方法名 = function(){//实现代码}
的形式创建对象的指定方法并且实现方法。
或者构造函数自定义某一类对象,如下:
// 通常形式下的构造方法
function Person(name,age){
this.name = name;
this.age = age;
}
// 添加方法
Person.prototype.speak = function(){
console.log("我可以说话!!");
}
// 创建对象
let p1 = new Person("小明",29);
p1.speak(); // 我可以说话!!
console.log(p1); // Person { name: '小明', age: 29 }
方式一:点访问
点后面直接跟的是对象的属性,如果属性存在可以访问到,如果属性不存在,得到undefined。
person.name //"terry"
方式二:中括号访问
中括号中放的是变量,中括号可以将该变量进行解析。并且变量要用双引号包起来。原因请看这
person["name"]; //terry
方法的访问主要是为了执行该对象中的方法,需要按照函数调用的方式去使用。
//以下结果不一样
person.sayName; //无输出,并不是调用方法
person.sayName(); //my name is jack 方法的调用,方法名后面一定要加上括号才表示调用
普通for循环是不能遍历对象的。
增强版的for…in循环可以遍历对象的属性和方法,也可以遍历数组(需要注意的是for…in遍历出来的是数组的索引值)
格式:
for(自定义变量名 in 数组/对象){
//执行代码;
}
例如:
var person = {
name : "jack",
age : 12,
sayName:function(){
console.log("my name is",this.name);
}
};
//自定义变量名key使用来指定数组的索引或者对象的属性和方法
for(var key in person){
var value = person[key];
//var value = person.key;
console.log(key + ' ' + value);
}
结果:
name jack
age 12
sayName [Function: sayName]
补充一:通过增强for循环可以从数组中依次获取数组的索引,或者依次从对象中获取到属性名,从以上结果可以发现,方法也被遍历出来了,不过并没有被调用,只是以
Function:方法名
的形式显示出来了。
补充二:为什么再增强for循环里面的var value = person[key];
语句中的key不用加引号,因为这个key是个变量,专门用来存储person对象中的属性名,如果加上引号,则表示key是person中的属性,这样就会返回undefined。
还有一种方式,用到了Object对象的扩展方法Object.keys(obj),该方法用于获取所有可枚举属性名,并且返回一个由给定对象自身可枚举属性组成的数组,然后利用数组中的forEach方法进行遍历
var person = {
name : "jack",
age : 12,
sayName:function(){
console.log("my name is",this.name);
}
};
Object.keys(person).forEach(function(item){
console.log(item, person[item]);
})
// 在这里通过keys()方法返回的数组为['name','age','sayName'],也就是对象中的属性名组成的数组
Object中还有一个方法Object.values(),该方法可以获取所有可枚举属性的属性值,并返回一个给定对象自身的所有可枚举属性值的数组
格式如下:
对象名.新添属性名="属性值";
只能删除对象自身拥有的属性,格式如下:
delete 对象名.属性; //点方式
delete 对象名["属性"]; //中括号方式
ECMAScript中可用的3种强制类型转换如下:
1、Boolean(value)
作用:将给定的值转换为Boolean类型
例如:将Object类型转换为Boolean
var obj = {
name: "jack",
age : 21
};
console.log(typeof(obj)); //object
//使用Boolean包装器进行转换
console.log(Boolean(obj)); //true,表示转换成功
console.log(typeof(obj)); //object
console.log(typeof(Boolean(obj))); //boolean
2、String(value)
作用:把指定的值转换成字符串。
例如:将Object类型转换为String类型
var obj = {
name: "zhangsan",
age : 12,
//这里是重写了toString方法
toString:function(){
return this.name + "--" + this.age;
}
};
//语句1
console.log(obj.toString(), typeof(obj.toString())); //zhangsan--12 string
//语句2
console.log(String(obj), typeof String(obj)); //zhangsan--12 string
分析:
(1)语句一第一个部分在obj中调用了重写的toString()方法,该方法返回当前对象中的name和age属性。后面一个typeof(obj.toString())
语句:这里因为toString()方法重写,返回的是一个字符串,基本类型,所以结果为string。
(2)语句2中通过String()
将obj转换为了String类型。
这里简单介绍下Object对象中的toString()方法和valueOf()方法
toString()方法:
Object下面的toString()方法有几个作用:
(1)在未进行自定义重写时(直接从Object继承过来时),会将对象转换为一个原始值[object Array]
(2)检测对象的类型
Object.prototype.toString.call(arr)==="[object Array]"
(3)继承Object的类型可以通过重写toString()方法后自己定义toString()的作用。
valuef()方法:
Object下面的valueOf()方法默认用于返回指定对象的原始值,若对象没有原始值,则将返回对象本身。const obj = { foo: 1 }; console.log(obj.valueOf() === obj); // true console.log(obj.valueOf()); // { foo: 1 }
3、Number(value)
作用:把给定的值转换成数字(可以是整数,也可以是浮点数)
var obj = {
name:"jack",
age:12,
/*
1.如果只重写了valueOf()和toString()方法中的其中一个,则调用被重写的方法,并将返回值用Number()转换。
2.如果两个方法都重写了,则调用valueOf(),并将返回值用Number()转换。
3.如果两个方法都没有重写,则返回NaN
*/
toString:function(){
return "100";
},
valueOf:function(){
return 10;
}
};
console.log(Number(obj)); //10
分析:先调用toString方法,接着判断该方法的返回值是否为基本数据类型,是,则按照相应数据类型的转换规则对其进行转换,若不是,则再该返回值的基础上继续调用valueOf方法,判断valueOf的返回值是否为基本数据类型,是,则按照相应数据类型的转换规则对其进行转换,若不是基础类型则报错。
检测一个属性是否属于某个对象。常用的方式主要有3种:
作用:检测某属性是否是某对象的自有属性或者是继承属性
var obj = {
name: 'zhangsan',
age: 18,
school: 'xx大学'
}
//in运算符的左侧为属性名称,右侧为对象
console.log('name' in obj); //true
console.log('age' in obj); //true
console.log('gender' in obj); //false
//=================================================================
//如果用in判断一个属性存在,这个属性不一定是obj的,它可能是obj继承得到的,如:
'toString' in obj; // true
//因为toString定义在object对象中,而所有对象最终都会在原型链上指向object,所以obj也拥有toString属性。
作用:检测给定的属性是否是对象的自有属性,对于继承属性将返回false
var obj = {
name: 'zhangsan',
age: 18,
school: 'xx大学'
}
console.log(obj.hasOwnProperty('name')); //true
console.log(obj.hasOwnProperty('age')); //true
console.log(obj.hasOwnProperty('toString')); //false,toString为继承属性
console.log(obj.hasOwnProperty('gender')); //false
propertyIsEnumerable()是hasOwnProperty()的增强版,除了是自身属性外,还要求是可枚举属性,可枚举属性即我们创建的属性可以通过增强for…in遍历出来。
var obj = {
name: 'zhangsan',
age: 18,
school: 'xx大学'
}
console.log(obj.propertyIsEnumerable('name')); //true
console.log(obj.propertyIsEnumerable('age')); //true
console.log(obj.propertyIsEnumerable('toString')); //false,不可枚举
console.log(obj.propertyIsEnumerable('gender')); //false
object.prototype是构造函数的原型对象,具体看下面介绍。
ECMAScript中有两种属性:数据属性、访问器属性。这两种属性用于设置属性的高级属性,例如该属性是否可以配置,是否可以读写,是否可以遍历,并且可以通过setter,getter来监听数据的改变。
数据属性中的特性如下:
一、【Configurable】
作用:
(1)表示是否通过delete删除属性从而重新定义属性(默认为true,当为false时,不能重新定义属性特性能否使用delete删除)
(2)表示能否修改属性的特性(默认为true)
(3)表示能否把属性修改为访问器属性(默认为true)
二、【Enumerable】
表示能否通过for-in循环返回属性。(属性直接定义在对象中默认为true)
三、【Writable】
表示能够修改属性的值。(属性直接定义在对象中默认为true)
四、【Value】
修改后的取值。
必须使用ECMAScript5中的Ojbect.defineProperty方法。
语法:Object.defineProperty(obj, prop, descriptor;
参数说明:
1、obj:目标对象(必需)
2、prop:需定义或者修改的属性的名字(必需)
3、descriptor:目标属性所拥有的特性(必需)
返回值:所传入的函数的对象,及方法中的第一个参数obj
例如:
var obj = {
name : "jack",
age : 29,
gender : "male"
}
consolo.log(obj.age); //29
//修改对象obj中age属性的特性,将age属性的值修改为30
Object.defineProperty(obj,"age",{
configurable:true, //该属性可否通过delete删除对象中的值。
enumerable:true, //该属性的值可否枚举
writable:true, //该属性的值可否修改
value:"30" //将jack的age属性的值修改为30
})
consolo.log(obj.age); //30
每次创建对象并为对象设置属性时,该属性的默认特性Configurable、Enumerable、Writeable都默认为true,value为该属性设置的值。
补充:
object.defineProperties()
方法
作用:可以用来修改对象中的多个属性的特性
语法:Object.defineProperties(obj, props)
参数:obj,目标对象;props,该对象的一个或者多个键值对定义了将要为对象添加或者修改的属性的具体配置
返回值:传入函数的对象,即第一个参数obj
例如:
var obj = new Object();
Object.defineProperties(obj, {
name: {
value: 'zhangsan',
configurable: false,
writable: true,
enumerable: true
},
age: {
value: 18,
configurable: true
}
})
console.log(obj.name, obj.age) // zhangsan, 18
Object.defineProperties()方法也可以用来新添属性
作用:返回指定对象一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)
语法:Object.getOwnPropertyDescriptor(obj, prop)
参数介绍:
obj:所查找的目标对象
prop:目标对象内属性的名称
例如:
var person = {
name: '张三',
age: 18
}
var desc = Object.getOwnPropertyDescriptor(person, 'name');
console.log(desc);
//结果如下
// {
// configurable: true,
// enumerable: true,
// writable: true,
// value: "张三"
// }
补充:
Object. getOwnPropertyDescriptors()
作用:返回所指定对象的所有自身属性的描述符,如果没有任何自身属性,则返回空对象。
语法:Object.getOwnPropertyDescriptors(obj)
例如:
var person = {
name: '张三',
age: 18
}
var desc = Object.getOwnPropertyDescriptors(person);
console.log(desc)
//结果如下
//{
// name: { value: '张三', writable: true, enumerable: true, configurable: true },
// age: { value: 18, writable: true, enumerable: true, configurable: true }
//}
介绍:
这个属性不包含数据值,包含的是一对get和set方法,在读写访问器属性时,就是通过这两个方法来进行操作处理的。
访问器属性包含的四个特性:
1、【Configurable】
作用:
(1)表示能否通过delete删除属性从而重新定义属性
(2)表示能否修改属性的特性
(3)表示能否把属性修改为访问器属性
(4)默认值为false
2、【Enumerable】
作用: 表示能否通过for-in循环返回属性,默认为false
3、【Get】
作用:在读取属性时调用的函数,默认值为undefined
4、【Set】
作用:在写入属性时所调用的函数,默认值为undefined
注意: 访问器属性不能直接定义,要通过Object.defineProperty()这个方法来定义。
例如:
var book = {
_year: 2020, //属性名前加上下划线表示是内部属性,只能通过对象的方法来读写
editor: 1
};
Object.defineProperty(book, 'year', {
get: function () {
return this._year;
},
// 若只指定get方法,不指定set方法,那就默认该属性是只读的
set: function (newYear) {
if (newYear !== this._year) {
this._year = newYear
this.editor ++
}
}
});
// 测试访问属性中的get,set方法
console.log('修改前的year:' + book.year);
book.year = 2021;
console.log('修改后的year:' + book.year);
console.log('修改editor次数' + book.editor);
// 访问器属性可以通过Object.getOwnPropertyDescriptor()查询
console.log(Object.getOwnPropertyDescriptor(book, '_year'));
结果如下:
修改前的year:2020
修改后的year:2021
修改editor次数2
{ value: 2021, writable: true, enumerable: true, configurable: true }
数据的双向绑定:
在一个对象(book)中设置一个私有属性(_year:开头下划线代表私有属性),再为这个对象设置访问器属性year(本身还未在对象中定义),当book.year进行修改时触发set函数,通过这个函数可以进行数据的操作,比如数据的判断赋值等一系列操作,从而实现数据的双向绑定。这个原理是vue的本质原理。vue是数据驱动框架,当数据发生改变的时候,视图自动更新。
首先我们知道,js是面向对象的语言,也就是说js里面一切皆可为对象,函数可以是对象,构造函数也是函数,所以构造函数也是对象,既然是对象,那么它身上就会有属性,而在构造函数中有这么一个属性,叫prototype,它指向一个叫xxx.prototype的对象,这个对象就是原型对象。
简单来说,每一个JavaScript对象(null除外)在创建的时候就会自动与之关联一个对象(Object.prototype),这个对象就是我们所说的原型对象,每一个对象都会从原型对象"继承"属性。
function Foo(){} // 构造函数
console.log(Foo.prototype); // Foo{} // 构造函数的prototype指向原型对象Foo{}
function Foo(){} // 构造函数
console.log(Foo.prototype); // Foo{} // 构造函数的prototype指向原型对象Foo{}
console.log(Foo.constructor); // [Function: Function] // 构造函数的constructor指向自身
console.log(Foo.prototype.constructor); // [Function: Foo] // 原型对象Foo.prototype上的属性constructor指向构造函数Foo()
例如有这么一个构造函数Foo,new Foo();
表示创建一个对象实例。
function Foo(){}
let f = new Foo() // f就是一个对象实例或者叫实例对象
通过上述描述,咱们知道,构造函数上是有prototype属性指向原型对象的,那么为什么说通过构造函数创建的实例对象会继承原型对象上的属性或方法呢,难道实例对象上也有prototype属性吗,其实不然,实例对象上是没有prototype这个属性的,但它有个一个名叫隐式原型的属性__proto__,简单来说,proto 属性是实例对象所继承的原型链,而 prototype 属性则是构造函数所拥有的原型对象。它们之间的关系是,当我们使用一个构造函数创建一个实例对象时,该实例对象的 proto 属性会指向构造函数的 prototype 属性,从而使得该实例对象可以继承构造函数原型对象上的方法和属性。我们可以叫它对象的原型,或者隐式原型。
看如下代码:
function Foo() { } // 构造函数
var f = new Foo(); // 创建对象实例
console.log(f.constructor); // [Function :Foo] // 对象实例的constructor指向构造函数
console.log(f.prototype) // undefined // 实例对象上找不到prototype属性
console.log(f.__proto__); // Foo{} // 对象实例的__proto__属性指向实例对象
console.log(Foo.prototype); // Foo{} // 构造函数的prototype指向原型对象Foo{}
console.log(Foo.constructor); // [Function: Function] // 构造函数的constructor指向自身
console.log(Foo.prototype.constructor); // [Function: Foo] // 原型对象上的属性constructor指向构造函数
但需要注意的是,这个__proto__只出现在浏览器上,并且不是所有的浏览器都有这个属性,因此,并不推荐在代码中使用这个属性,并且这是官方不承认的,但却能帮助理解原型链的一个东西。
主要作用是让对象实例可以继承原型对象中的属性和方法,达到共享的效果。例如下面两个例子:
例1
//构造函数
var person = function(name){
this.name = name;
};
//原型对象
person.prototype.getName = function(){
return this.name;
}
var name = new person("jack"); //创建一个对象实例
name.getName(); //jack
分析:通过给原型对象person.propotype设置一个函数对象的方法,使得person实例继承了这个方法,并且实例可以调用该方法。
再看一个例子:
function Person(){}
Person.prototype.name = 'jack';
Person.prototype.age = 18;
Person.prototype.dream = function(){
console.log("Get a good job!");
}
var person1 = new Person(); //创建实例对象person1
person1.dream(); //Get a good job!
var person2 = new Person(); //创建实例对象person2
person2.dream(); //Get a good job!
//判断两个实例继承的方法和属性是否全等
console.log(person1.dream === person2.dream); //true
console.log(person1.age === person2.age); //true
可以看出 person1和person2访问的都是同一个dream()函数,即他们的属性和方法都是共享的。
原型对象和对象原型说的是一个东西,没区别。
补充:
(1)在Object的构造函数的原型对象中的属性和方法都可以被Object构造函数的实例所继承。
(2)Object原型中的所具有的任何属性和方法也同样存在于其他对象中,任何对象继承自Object。
作用:用来描述对象之间的关系。
第一种解释:
一个对象,它有许多属性,其中有一个属性指向了另外一个对象的原型属性;而后者也有一个属性指向了再另外一个对象的原型属性。这就像一条一环套一环的锁链一样,并且从这条锁链的任何一点寻找下去,最后都能找到链条的起点,即 Object;因此,我们也把这种机制称作:原型链。
第二种解释:
当访问一个实例对象的某个属性或方法时,会先查找实例对象自身有没有这个属性或方法,如果没有找到,则会沿着它的__proto__隐式原型上查找,即找到它的构造函数的prototype,也就是它的原型对象,如果还没有找到就会再沿着构造函数的prototype的__proto__中查找,这样一层一层向上查找就会形成一个链式结构,直到找到链条的起点,也就是object的原型object.prototype上,而object.prototype指向null,我们称为原型链。
原型链遵循如下规则:
1、对象有_proto_属性,函数有prototype属性
2、对象由函数生成;
3、生成对象时,对象的_proto_属性指向函数的prototype属性
4、函数也是对象,那么函数里面也有_proto_属性
// 使用构造函数创建实例对象
var obj = new Object();
// 调用原型对象中继承的方法
console.log(obj.toString()); //[object Object]
console.log(obj.__proto__.toString()); //[object Object]
// 构造函数 Object
console.log(Object); //[Function: Object]
// 原型对象 Object.prototype
console.log(Object.prototype); //[Object: null prototype] {}
// 原型对象中的constructor属性 原型对象中的constructor属性指向构造函数
console.log(Object.prototype.constructor); //[Function: Object]
// 实例__proto__ 指向 原型对象
console.log(obj.__proto__ === Object.prototype); //true
//================================================================
// 创建Date对象
var now = new Date();
console.log(now); //2022-04-02T16:13:56.536Z
// 使用原型对象中的方法
console.log(now.toString()); //Sun Apr 03 2022 00:13:56 GMT+0800 (中国标准时间)
console.log(now.toLocaleString()); //2022/4/3 00:13:56
总结:
每个构造函数都对应一个原型对象,原型对象都包含一个指向构造函数的指针,实例对象都包含一个指向原型对象的内部指针。
(1)函数才具有prototype属性,这个属性是指针,指向一个包含所有实例共享的属性和方法的对象,这个对象也就是原型对象。
(2)原型对象也有一个属性,叫做constructor,这个属性包含了一个指针,指向构造函数。
(3)js中的所有对象都具有_proto_属性,用于指向构造该对象的构造函数的原型。
但web官方并不承认_proto_属性,web官方所以目前它的存在只是帮助你稍微好理解构造函数、对象实例、原型对象间的关系。
Object.propotype.constructor();
作用:保存用户创建当前对象的函数,与原型对象对应的构造函数。
Object.propotype.hasOwnProperty(propertyName) ;
作用:检查给定的属性名是否是对象的自有属性。
Object.propotype.propertyIsEnumerable(propertyName);
作用:检查给定的属性在当前对象实例中是否存在。
Object.propotype.valueOf();
作用:返回对象的字符串,数值,布尔值的表示。
Object.propotype.toLoacaleString();
作用:返回对象的字符串表示,该字符串与执行环境的地区对应。
Object.propotype.toString();
作用:返回对象的字符串表示
Object.propotype.isPrototypeOf(object);
作用:检查传入的对象的原型。
对象序列化是指将对象的状态转换为字符串。同样的,也可以将字符串还原为对象函数,这样的过程成为反序列化。
特殊处:
(1)RegExp,Error对象,undefined值不能序列化和反序列化。
(2)JSON.stringify(obj) 将对象序列化为JSON字符串,只能序列化对象可枚举的自有属性
(3)JSON.parse(jsonStr) 反序列化
// 将对象转换为JSON字符串
// {"name":"jack","age":12}
var obj = {
name:"jack",
age:12
};
console.log(obj); //object类型打印的结果 {name:'jack',age:12}
// 将对象转换为JSON字符串
var json = JSON.stringify(obj);
console.log(json);//转换为String类型的字符串:{"name":"jack","age":12}
// 将JSON字符串转换为对象
var obj = JSON.parse(json);
console.log(obj); //{ name: 'jack', age: 12 }
浏览器中的全局对象是window对象
node环境下的全局对象是global对象