对象只是一种特殊的数据。对象拥有属性和方法。
JavaScript 中的所有事物都是对象:字符串、数值、数组、函数…
此外,JavaScript 允许自定义对象。
JavaScript 提供多个内建对象,比如 String、Date、Array 等等。 对象只是带有属性和方法的特殊数据类型。
属性是与对象相关的值。
访问对象属性的语法是:
objectName.propertyName
这个例子使用了 String 对象的 length 属性来获得字符串的长度:
var message="Hello World!";
var x=message.length;
在以上代码执行后,x 的值将是:
12
访问对象的方法
方法是能够在对象上执行的动作。
您可以通过以下语法来调用方法:
objectName.methodName()
这个例子使用了 String 对象的 toUpperCase() 方法来将文本转换为大写:
var message="Hello world!";
var x=message.toUpperCase();
在以上代码执行后,x 的值将是:
HELLO WORLD!
通过 JavaScript,我们能够定义并创建自己的对象。
创建新对象有两种不同的方法:
Object 构造函数创建一个对象包装器。
Object 构造函数,会根据给定的参数创建对象,具体有以下情况:
语法格式:
// 以构造函数形式来调用
new Object([value])
vaule 可以是任何值。
以下实例使用 Object 生成布尔对象:
// 等价于 o = new Boolean(true);
var o = new Object(true);
示例
var obj = {
name : "林江涛",
age : 18,
gender: "男"
};
console.log(obj);
console.log(obj.name);
console.log(typeof obj);
{}就是对象的界定符,就是对象的字面量。对象有属性,所谓的属性就是这个对象的特点、特性,name、age、gender都是这个obj对象的属性(preperty)。
什么是对象?对象就是属性的无序集合。
我们可以用.点语法、方括号法来获得一个对象的属性。那么你会发现,和数组有点相似,只不过数组的下标只能是数字0、1、2……,而我们的对象,可以用任何的词儿来当做属性名。
公式:
{
k : v,
k : v,
k : v,
k : v
}
JSON和对象字面量的区别:
之前学习过JSON,JSON要求所有的k必须加引号,而对象字面量不需要加引号,当然加引号也不错。
JSON = JavaScript Object Notation,JS对象表示法。JSON是一个用于交换的格式,所以JSON不仅仅JavaScript用,后台语言比如PHP、Java、ASP等等都要识别JSON,为了最大的兼容,k必须加引号。也就是说,JSON里面的k加引号,不是因为JS,而是因为后台的那些语言。
JSON要比对象字面量,要严格,严格在哪儿呢?就是所有的k,必须加引号。
JSON:
{
"k" : v,
"k" : v,
"k" : v,
"k" : v
}
但是,下面的特殊情况,这个k必须加引号:
先说一下,上面这些情况,也同时不能使用点语法来访问属性了,必须使用方括号:
var obj = {
name : "树懒"
}
console.log(obj["name"]);
特殊形式的k,必须要加上引号,检索属性的时候,必须用方括号:
var obj = {
"24&*$@@)@!" : "哈哈",
"all name" : "六六六",
"++++%%%%" : "嘻嘻",
"var" : "么么哒",
"function" : "嘻嘻"
}
console.log(obj["24&*$@@)@!"]);
console.log(obj["all name"]);
console.log(obj["++++%%%%"]);
console.log(obj["var"]);
console.log(obj["function"]);
你会发现,JS会一个对象的属性名,没有特殊的规定。这是因为属性名不是标识符,没有那些规定。
对象的属性的访问,点语法是有局限的,它不能访问上面的特殊的那些情况。也不能访问以变量保存的k:
var obj = {
name : "林江涛",
age : 18,
gender : "男",
"study score" : 100
}
//console.log(obj."study score");//错误的
console.log(obj["study score"]); //正确的
console.log(obj["a" + "ge"]); //正确的
var a = "gender";
console.log(obj[a]); //正确的
效果
var obj = new Object(); //这是一个空对象,里面没有任何属性
obj.name = "林江涛";
obj.age = 18;
obj.gender = "男";
console.log(obj);
console.log(obj.age);
console.log(typeof obj);
new是一个运算符,你没有看错,和±*/一样是一个运算符。表示新创建一个对象。一会儿我们学习构造函数,实际上你将了解到new是一个函数调用的方式。Object()大写字母O,这是一个系统内置的构造函数,什么是构造函数,我稍后会在下面写到。
下面就可以用obj.k = v ;来追加属性了:
obj.name = "林江涛";
这是一条语句,如同:
oDiv.className = "current";
事实上,工程师更喜欢用字面量的方式来创建对象。因为更直观:
字面量方式:
var obj = {
name : "林江涛",
age : 18,
gender : "男"
};
构造函数方式:
var obj = new Object();
obj.name = "林江涛";
obj.age = 18;
obj.gender = "男";
上面两种方式创建出来的对象,是相同的。字面量的方式直观、简单、并且有“封装”的感觉。所以我鼓励大家用字面量来创建对象。
但是,不要杂糅:
var obj = {
}; //的确能创建一个空对象
obj.name = "林江涛"; //追加属性
obj.age = 18; //追加属性
obj.gender = "男"; //追加属性
对象属性值,可以是任何东西。比如数字、字符串、布尔值、正则表达式、对象、数组、函数……
var kaola = {
name : "林江涛",
age : 18,
peiou : {
name : "JiuMei",
age : 19
}
}
console.log(kaola.peiou.age);
var obj = {
a : 1,
b : "哈哈",
c : true,
d : /[A-Z]/g,
e : function(){
alert(1+2);
},
f : {
p : 1
}
}
特别的,当对象的属性的值是一个函数的时候,我们称这个函数是对象的方法。
当一个对象的属性的值,是一个函数,那么这个函数我们就称为对象的“方法”(method)。
方法就是一个对象能够做的事情,一般来说,就是一个动词。比如小明打招呼、长大方法。
var lin= {
name : "林江涛",
age : 18,
gender : "男",
sayHello : function(){
alert("你好,我是" + this.name);
alert("今年" + this.age + "岁了");
alert("我是可爱的小" + this.gender + "生");
}
}
lin.sayHello();
比如上面的案例,sayHello就是一个属性,只不过它的值是一个函数,所以我们就可以说lin这个对象,有sayHello方法。
一个对象,方法函数里面的this指的是这个对象。
那我们复习一下现在,我们调用函数的方式有哪些?这些调用函数的方式,里面的this又是谁?
说白了,我们学习“方法”,无非就是学习了一种函数的调用方式。这个函数里面的this指的是这个对象。
函数里面的this到底是谁,在函数定义的时候并不知道,要看函数如何被调用。
对象的方法的哲学,就是操作自己的属性。如果一个对象的方法,不操作自己的属性,那干嘛还要是方法呢?
zhangda方法,就是让自己的age++:
var lin= {
name : "林江涛",
age : 18,
gender : "男",
sayHello : function(){
alert("你好,我是" + this.name);
alert("今年" + this.age + "岁了");
alert("我是可爱的小" + this.gender + "生");
},
zhangda : function(){
this.age++;
}
}
JavaScript规定,一个函数可以用new关键字来调用。那么此时将按顺序发生四件事情:
function People(){
this.name = "林江涛";
this.age = 18;
this.gender = "男";
}
var lin= new People();
console.log(lin);
console.log(lin.age);
console.log(typeof lin);
上面的函数的机理解析:
var lin= new People(); //此时遇见了new操作符,计算机将做四件事
function People(){
//1)隐式创建一个空对象
this.name = "林江涛"; //2)函数里面的this指向这个创建的空对象
this.age = 18; //3)顺序执行函数体里面的语句
this.gender = "男";//4)执行完毕之后,返回这个对象
}
console.log(lin);
console.log(lin.age);
console.log(typeof lin);
函数使用new关键字来调用。
此时很有意思,函数不仅仅能够执行,还能返回出来一个对象。也就是说,对象是函数“生”出来的,对象是函数“new”出来的。
我们称呼这个函数,叫做构造函数,一般的,构造函数用大写字母开头。也叫作People“类”。
我们称呼lin这个对象是People类的实例。
所以我们的构造函数,也可以看成类的定义:
//类,People类
function People(name,age,gender){
this.name = name;
this.age = age;
this.gender = gender;
}
//new关键字造出来的xiaoming、xiaohong,我们称为People类的实例。
var lin= new People("林江涛",18,"男");
var jiu= new People("JiuMei",17,"女");
console.log(lin);
console.log(jiu);
People这个函数一会儿将用new关键字来调用,所以称为构造函数。
构造函数里面的语句将执行,并且将返回一个对象。所以new多少次,里面的语句就会执行多少次。宏观的看,返回的对象,就都有name属性、age属性、gender属性了。我们说,这些返回的对象都有相同的属性群了,所以可以看做是一类东西。那么People这个构造函数,也可以看成类的定义。
但是JavaScript中,没有类的概念。是通过构造函数的4步走机制来创建类似的对象,可以看为类。JS这个语言是“基于对象”(base Object)的语言,不能叫做“面向对象”(orinted object)的语言。
注意,我们学习Java、C++、C#语言的惯例,构造函数以大写字母开头。
function People(){
}
这是一个People类,这个函数一会儿要用new来调用。能返回对象。
类就是蓝图,上帝要根据这个蓝图造小人儿。造出的小人,就是People类的实例。
构造函数中,肯定会出现this语句,如果没有this语句,那就相当于不能给创建出来的空对象绑定属性。
构造函数,在JS中,就是一个普通的函数,JS没有对里面要书写什么进行任何规定。只不过我们工程师习惯的,先用this.*** = ***来罗列所有属性,然后罗列所有的方法而已。你一定要深刻理解:
new一个函数的时候,函数里面的语句会执行
总结:
宏观的看,new出来的东西都有相同的属性群、方法群。此时我们就发现了:哇塞这个构造函数像模板一样,可以非常快速的制作类似的实例。这个构造函数可以称为类,它new出来的东西叫做类的实例。
我们先来看一个事儿:
示例
function fun(){
alert("你好");
}
console.log(fun.prototype);
console.log(typeof fun.prototype);
在JavaScript中,任何一个函数,都有一个prototype属性,指向一个对象。我们输出了一个函数的prototype属性,你会发现是一个空对象。输出这个prototype的类型,发现是object类型。
prototype就是英语“原型”的意思。每个函数都有原型,原型是一个对象。
一个函数的原型,对于普通函数来说,没用。但是如果函数是一个构造函数,那么函数的原型,用处极大
//构造函数,构造函数里面没有任何语句,也就是说,这个构造函数在执行的时候,不会给创建出来的对象添加任何属性。
function People(){
this.name="JiuMei"
}
//构造函数的原型,我们更改了构造函数的原型,为一个新的对象:
People.prototype = {
name : "林江涛",
gender : "男",
age : 18
}
//当一个对象被new出来的时候,不仅仅执行了构造函数里面的语句,也会把这个函数的__proto__指向构造函数的prototype。
var lin= new People();
console.log(lin.__proto__);
console.log(lin.__proto__ == People.prototype);
//当我们试图访问name、gender、age属性的时候,身上没有。那么就去查找原型,原型身上有,就当做了自己的属性返回了。如果身上有原型上也有,就只使用身上有的
console.log(lin.name);
console.log(lin.gender);
console.log(lin.age);
效果
prototype一定是函数的属性!当这个函数是一个构造函数的时候,那么它new出来的对象,将以它的原型那个对象为new出来的实例的原型对象。
注意,任何一个对象,都有__proto__属性,这个属性是Chrome自己的属性,别的浏览器不兼容,但是别的浏览器也有原型对象,只不过不能通过__proto__进行访问而已。
这是属性指向自己的原型对象。
我们的JavaScript有一个非常牛逼的机制:原型链查找。
当我们试图访问一个对象身上的属性的时候,如果这个对象身上有这个属性,则返回它的值。如果它身上没有这个属性,那么将访问它的原型对象,检测它的原型对象身上是否有这个值,如果有返回它原型对象身上的这个值。
也就是说,我们刚才讲解了2个对象和一个函数的故事。任何一个函数都有原型,原型是一个对象,用prototype来访问。当这个函数是构造函数的时候,new出来的对象,它们的原型对象就是这个构造函数的原型。
prototype我们称为“原型”,只有函数有原型
__proto__我们称为“原型对象”,任何对象都有原型对象。
我们定义一个方法的时候,如果写在构造函数里面:
function People(name,age){
this.name = name;
this.age = age;
this.sayHello = function(){
alert("你好,我是" + this.name + "我今年" + this.age + "岁了");
}
}
var lin= new People("林江涛",12);
var jiu= new People("JiuMei",11);
lin.sayHello();
jiu.sayHello();
实际上这个函数被复制了两份,一份给了lin,一份给了jiu。lin和jiu这两个实例身上有了相同功能的函数,但是这个函数不是同一个函数!
而函数的意义就是被复用,你没有复用函数:
alert(lin.sayHello == jiu.sayHello); //false
lin身上的函数,和jiu身上的函数,不是同一个函数。
那应该怎么办呢?一句话:所有的属性要绑在对象身上,而所有的方法,定义在对象的原型对象中:
function People(name,age){
//构造函数里面,负责定义一些属性,随着构造函数的执行,这些属性将绑定到new出来的对象身上
this.name = name;
this.age = age;
}
//把所有的方法,定义在原型对象身上:
People.prototype.sayHello = function(){
alert("你好,我是" + this.name + "我今年" + this.age + "岁了");
}
alert(lin.sayHello == jiu.sayHello);
证明了lin的sayHello方法和jiu的,是同一个函数。内存消耗小很多。
先学一个属性,constructor,函数的原型有constructor属性,指向构造函数。
People.prototype.constructor //指向构造函数
我们之前说过,一个对象的原型身上有什么,那么实例对象就也可以打点调用什么,所以:
xiaoming.constructor //指向构造函数
只要是对象,一定有原型对象,就是说只要这个东西是个对象,那么一定有__proto__属性。
世界上只有一个对象没有原型对象,这个对象就是Object.prototype。
Object是一个函数,是系统内置的构造函数,用于创造对象的。Object.prototype是所有对象的原型链终点。
所以,当我们在一个对象上打点调用某个方法的时候,系统会沿着原型链去寻找它的定义,一直找到Object.prototype。
所有的引用类型值,都有内置构造函数。比如
new Object()
new Array()
new Function()
new RegExp()
new Date()
函数也是对象。JavaScript中函数是一等公民,函数是对象。函数也是对象,只不过自己能()执行。
基本类型值,也有包装类型。所谓包装类型,就是它的构造函数。
new Number()
new String()
new Boolean()