对象的定义
对象其实就是一组数据和功能的集合,其属性可以包含基本值、对象或者函数。在ECMAScript中,Obiect类型是所有它的实例的基础。Object的每个实例都有以下属性和方法。
- constructor: 保存着用于创建当前对象的函数
- hasOwnProperty(): 用于检查给定的属性在当前实例对象中是否存在
- isPrototypeOf(object): 用于检查传入的对象是否是当前对象的原型(ES5新增方法getPrototypeOf)
- propertyIsEnumerable(propertyName): 用于检查给定的属性是否能够用for-in语句来枚举
- toString(): 主要用于Array、Boolean、Date、Error、Function、Number等对象转化为字符串形式。日期类的toString()方法返回一个可读的日期和字符串
- toLocaleString(): 和toString()一样,但是该字符串与执行环境的地区对应
- valueOf(): 返回它相应的原始值,通常与toString()方法的返回值相同。而日期类定义的valueOf()方法会返回它的一个内部表示:1970年1月1日以来的毫秒数.
var createPerson = function(){
this.name = 'zhangsan'
}
createPerson.prototype.color = 'red';
var person = new createPerson();
var date = new Date();
person.age = 20;
console.log(person.constructor); //ƒ () {this.name = 'zhangsan'}
createPerson.hasOwnProperty('color'); //false
createPerson.hasOwnProperty('name'); //true
createPerson.prototype.isPrototypeOf(person);//true
person.propertyIsEnumerable('age');//true
person.propertyIsEnumerable('toString'); //false
console.log(date.toLocaleString());//2018/3/26 上午11:18:04
console.log(date.toString());//Mon Mar 26 2018 11:18:04 GMT+0800 (CST)
console.log(date.valueOf());//1522034284511
复制代码
创建object实例的方式有两种,第一种是new操作符后跟Object构造函数,还有一种就是对象字面量表示法。
var person1 = new Object();
person1.name = '张三';
console.log(person1);//{name: '张三'}
var person2 = {name: '李四'};
console.log(person2);//{name: '李四'}
复制代码
访问对象属性有两种方法,一种是点表示法,还有一种方括号表示法(可以通过变量来访问属性)。可以通过delete删除属性
var person1 = {name:'张三'};
var key = 'name';
console.log(person1.name);//张三
console.log(person1[key]);//张三
console.log(person['name']);//张三
delete person1.name;
console.log(person['name']);//undefined
复制代码
对象遍历:
- for...in循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)
- Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。
- Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。
- Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有 Symbol 属性的键名。
- Reflect.ownKeys返回一个数组,包含对象自身的所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。
Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)
var id = Symbol('id');
var person = {name:'张三',age: 20,[id]:'666'};
for(var key in person){
console.log('key='+key,'value='+person[key]);
//key=name value=张三 key=age value=20
}
console.log(Object.keys(person));//["name", "age"]
console.log(Object.getOwnPropertyNames(person));//["name", "age"]
console.log(Object.getOwnPropertySymbols(person));//[Symbol(id)]
console.log(Reflect.ownKeys(person));//["name", "age", Symbol(id)]
var newPerson = {};
Object.assign({},person);
console.log(newPerson);//{name: "张三", age: 20, Symbol(id): "666"}
复制代码
虽然Object构造函数或者对象字面量都可以穿件单个对象,但是这些方式有个明显的缺点:使用同个接口创建很多对象,会产生大量的重复代码。可以通过如下方式解决这个问题。
- 工厂模式
工厂模式其实是软件领域中一种广为人知的一种设计模式,这种模式抽象了创建具体对象的过程。例子如下:
function createPerson(name, age) {
var person = new Object();
person.name = name;
person.age = age;
person.sayName = function() {
console.log(this.name);
}
return person;
}
var person1 = createPerson('张三',20);
var person1 = createPerson('李四',22);
console.log(person1.sayName());//张三
复制代码
- 构造函数模式 ECMAScript中的构造函数(函数名以大写字母开头,用于区分ECMAScript中的其他函数)可用来创建特定类型的对象,调用构造函数会经历以下四步:
- 创建一个新对象
- 将构造函数的作用域给新对象
- 执行构造函数中的代码
- 返回新对象
function CreatePerson(name, age) {
this.name = name;
this.age = age;
this.sayName = function() {
console.log(this.name);
}
};
var person1 = new CreatePerson('张三',20);
var person2 = new CreatePerson('李四',22);
console.log(person1.sayName());//张三
复制代码
- 原型模式
我们创建的每个函数都有一个 prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。如果按字面意思来理解,那么prototype就是通过调用构造函数而创建的那个对象实例的原型对象。使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。换句话说,不必在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中。
function CreatePerson() {
};
CreatePerson.prototype.name = '张三';
CreatePerson.prototype.age = 20;
CreatePerson.prototype.sayName = function(){
console.log(this.name);
}
var person1 = new CreatePerson();
person1.sayName(); //张三
复制代码
- 组合使用构造函数和原型模式
构造函数用于与解决初始化参数(实例属性的定义), 原型模式用于共享方法和constructor。这种构造函数与原型组合的模式创建自定义类型,是ECMAScript中使用最广泛、认同度最高的一种创建自定义类型的方法。可以说,这是用来定义引用类型的一种默认模式
function CreatePerson(name, age) {
this.name = name;
this.age = age;
};
CreatePerson.prototype = {
constructor: CreatePerson,
sayName: function(){
console.log(this.name);
}
}
var person1 = new CreatePerson('张三', 20);
person1.sayName(); //张三
复制代码
- 原型动态模式
它把所有信息都封装在了一个构造函数中,而通过在构造函数中初始化原型(仅在必要的情况下),又保持了同时使用构造函数和原型的优点。
function CreatePerson(name, age) {
this.name = name;
this.age = age;
if(typeof this.sayName != 'function'){
CreatePerson.prototype.sayName = function() {
console.log(this.name);
}
}
};
var person1 = new CreatePerson('张三', 20);
person1.sayName();// 张三
复制代码
- 寄生构造函数模式
这种模式的基本思想是创建一个函数,该函数的作用仅仅是封装创建对象的代码。与工厂模式的区别:- 寄生模式创建对象时使用了New关键字
- 寄生模式的外部包装函数是一个构造函数
function Person(age,name) {
var o=new Object();
o.age=age;
o.name=name;
o.sayName=function(){
console.log(this.name);
}
return o;
}
var person=new Person(22,'张三');
person.sayName();//张三
复制代码
- 稳妥构造函数模式 道格拉斯 * 克罗克福德发明了JavaScript中的稳妥对象这个概念.所谓稳妥对象,指的是没有公共属性,而且其方法也不引用this的对象。稳妥对象最适合用在一些安全的环境中(这些环境会禁止使用new和this),或者防止数据被其他的应用改动。稳妥构造函数与寄生构造函数模式类似,但是也有两点区别:
- 稳妥模式不使用new操作符调用构造函数
- 新创建对象的实例方法不引用this
function Person(name,age) {
//创建要返回的对象
var o=new Object();
//可以在这里定义私有变量和函数
//添加方法
o.sayName=function(){
console.log(name);
}
//返回对象
return o;
}
var person=Person("张三",22);
person.sayName();
复制代码
函数的定义
函数的定义有三种方式:
- 函数声明
function say () { console.log('hello world'); } say(); // 函数调用 // 函数声明会引起函数提升:函数可以在声明之前调用 calc(2,3); // 6 function calc (a, b) { return a * b; } 复制代码
- 函数表达式
var say = function () { console.log('hello world'); } say(); // 函数调用 复制代码
- Function() 构造函数
var calc = new Function ( 'a', 'b', 'return a * b' ) calc(2, 3); // 函数调用 复制代码
函数参数
-
显式参数(Parameters): 在函数定义时列出
function calc (parameter1, parameter2, parameter3) { // parameter1、parameter2、parameter3为形参 return parameter1 + parameter2 + parameter3; } calc(1, 2, 3); // 1、2、3为实参 复制代码
-
隐式参数(Arguments): 在函数调用时传递给函数真正的值,即存放实参的列表
arguments是个类数组,本质上是一个对象,如果需要调用 数组的方法,可以使用 Array.prototype.方法名.call(arguments, 参数)
属性:
-
length
表示实参的个数
-
callee
表示当前指向的函数,一般用于递归函数
function sumAll() { var i, sum = 0; console.log(Array.prototype.slice.call(arguments, 0, 2)); // 打印出前两个实参的值 console.log(arguments.callee); // 打印出 sumAll这个方法 for (i = 0; i < arguments.length; i++) { sum += arguments[i]; } return sum; } sumAll(1,2,3,4); // 10 复制代码
-
参数特性:
-
参数的类型没有限制
-
函数对参数个数没有限制:
- 当 实参个数 < 形参个数,多余的形参 = undefined
- 当 实参个数 > 形参个数, 多余的实参被忽略
带有返回值的函数
有时,我们会希望函数将值返回调用它的地方。
通过使用 return 语句就可以实现。
在使用 return 语句时,函数会停止执行,并返回指定的值。
```
function myFunction()
{
var x=5;
return x;
alert('say something'); // return后面的语句不会执行
}
console.log(myFunction()); // 5
```
复制代码
函数调用
-
直接调用:函数名(实参)
calc(2, 3); // 相当于window.calc(2, 3); 复制代码
-
在a链接中调用:
<a href='javascript: 函数名()'>描述文字</a>
<a href='javascript:say()'>点我有惊喜</a> 复制代码
-
在事件中调用:
<html标签 事件类型='函数名()'>
<div onclick='say()'></div> 复制代码
-
自执行函数
// 写法1: (function say() { console.log('hello world'); })() // 写法2: (function say() { console.log('hello world'); }()) // 写法3:有返回值 true !function say() { console.log('hello world'); }() // 写法4:有返回值 NaN +function say() { console.log('hello world'); }() // 写法5:没有返回值 void function say() { console.log('hello world'); }() // 上面这些代码和下面是等价的 var say = function () { consoe.log('hello world'); } say(); 复制代码
-
递归调用: 在函数体内调用函数自身
// 递归算出n的阶乘 function calc (n) { if (n === 1) { return 1; } return n * calc(n-1); } calc(4); // 24 复制代码