JS原生对象
我们可以通过一个贪吃蛇小案例来分析js的对象使用和对比js过程开发
贪吃蛇小案例:https://github.com/GitHubKing89/JavaScript-Object/blob/master/JS贪吃蛇
JavaScript中没有类的概念,只有对象。
在JavaScript中定义对象可以采用以下几种方式:
1.基于已有对象扩充其属性和方法
2.工厂方式
3.构造函数方式
4.原型(“prototype”)方式
5.动态原型方式
方式一:
<script type="text/javascript">
var object = new Object();
//新增属性
object.name = "zhangsan";
//新增方法
object.sayName = function(name)
{
this.name = name;
alert(this.name);
}
//方法调用
object.sayName("lisi");
</script>
这种方式的弊端:这种对象的可复用性不强,如果需要使用多个对象,还需要重新扩展其属性和方法。
方式二:
//工厂方法这里可以看做是函数封装
function createObject()
{
var object = new Object();
object.username = "zhangsan";
object.password = "123";
object.get = function()
{
alert(this.username + ", " + this.password);
}
return object;
}
//创建对象
var object1 = createObject();
var object2 = createObject();
object1.get();//调用方法
方式二改进1;
function createObject(username, password)//传入参数
{
var object = new Object();
object.username = username;
object.password = password;
object.get = function()
{
alert(this.username + ", " + this.password);
}
return object;
}
var object1 = createObject("zhangsan", "123");
object1.get();
方式二改进2;
//共用函数
function get()
{
alert(this.username + ", " + this.password);
}
//函数对象只有一份
function createObject(username, password)
{
var object = new Object();
object.username = username;
object.password = password;
object.get = get; //每一个对象的函数对象都指向同一个函数对象
return object;
}
var object = createObject("zhangsan", "123");
var object2 = createObject("lisi", "456");
object.get();
object2.get();
优点:让一个函数对象被多个对象所共享,而不是每一个对象拥有一个函数对象。
缺点:对象和它的方法定义分开了,可能会造成误解和误用。
方式三
`**
构造函数的定义方法其实和普通的自定义函数相同。
**`
function Person()
{
//在执行第一行代码前,js引擎会为我们生成一个对象
this.username = "zhangsan";
this.password = "123";
this.getInfo = function()
{
alert(this.username + ", " + this.password);
}
//此处有一个隐藏的return语句,用于将之前生成的对象返回
//只有在后面用new的情况下,才会出现注释所述的这两点情况
}
//生成对象
var person = new Person();//用了new
person.getInfo();
带参改进
function Person(username, password)
{
this.username = username;
this.password = password;
this.getInfo = function()
{
alert(this.username + ", " + this.password);
}
}
var person = new Person("zhangsan", "123");
person.getInfo();
方式四(对象原型prototype
)
function Person(){
}
Person.prototype.username = "zhangsan";
Person.prototype.password = "123";
Person.prototype.getInfo = function() {
alert(this.username + ", " + this.password);
}
var person = new Person();
var person2 = new Person();
person.username = "lisi";
person.getInfo();
person2.getInfo();
如果使用原型方式来定义对象,那么生成的所有对象会共享原型中的属性,这样一个对象改变了该属性也会反映到其他对象当中。
单纯使用原型方式定义对象无法在构造函数中为属性赋初值,只能在对象生成后再去改变属性值。
对象原型改进
使用原型+构造函数方式来定义对象
,对象之间的属性互不干扰,各个对象间共享同一个方法。
<script type="text/javascript">
//使用原型+构造函数方式来定义对象
function Person() {
this.username = new Array();
this.password = "123";
}
Person.prototype.getInfo = function(){
alert(this.username + ", " + this.password);
}
var p = new Person();
var p2 = new Person();
p.username.push("zhangsan");
p2.username.push("lisi");
p.getInfo();
p2.getInfo();
</script>
方式五(只是改进了prototype
方法的重复问题)
在构造函数中通过标志量让所有对象共享一个方法,而每个对象拥有自己的属性。
<script type="text/javascript">
function Person() {
this.username = "zhangsan";
this.password = "123";
if(typeof Person.flag == "undefined") {
//此块代码应该只在第一次调用的时候执行
alert("invoked");
Person.prototype.getInfo = function() {
//这个方法定义在原型中,会被每一个对象所共同拥有
alert(this.username + ", " + this.password);
}
Person.flag = true;//第一次定义完之后,之后的对象就不需要再进来这块代码了
}
}
var p = new Person();
var p2 = new Person();
p.getInfo();
p2.getInfo();
</script>
js对象的拷贝
浅拷贝
**浅拷贝分两种情况,一是直接拷贝源对象的引用,二是源对象拷贝实例,但其属性(类型为Object、Array的属性)拷贝引用。**
对于对象实例的拷贝,常用的方法有:Array.prototype.slice(), Array.prototype.concat(), jQuery的$.extend({},obj)
例1:
var a = [{c:1}, {d:2}];
var b = a.slice();
console.log(a === b); // 输出false,说明外层数组拷贝的是实例
a[0].c = 3;
console.log(b[0].c); // 输出 3,说明其元素拷贝的是引用
例2:
<script>
// 对象的拷贝
var obj1 = {
name: 'zs',
age: 18,
sex: '男',
dog: {
name: '金毛',
age: 2,
yellow: '黄色'
}
}
var obj2 = {};
// 封装函数 - 把o1 的成员,复制给o2
// 浅拷贝
function copy(o1, o2) {
for (var key in o1) {
o2[key] = o1[key];
}
}
copy(obj1, obj2);
// 修改obj1中的成员
obj1.name = 'xxxx';
obj1.dog.name = '大黄';
console.dir(obj2);//输出结果:Object age :18 (dog: Object ----> age: 2 name: "大黄" yellow: "黄色" __proto__: Object )
name : "zs" sex : "男"
</script>
案例想说的是浅拷贝只能对简单属性copy,嵌套对象只是对象引用
function extend(parent, child) {
for (var key in parent) {
// 不给wsc复制同名的属性
if (child[key]) {
continue;
}
child[key] = parent[key];
}
}
深拷贝
深拷贝后,两个对象,包括其内部的元素互不干扰。常见的方法有JSON.parse(),JSON.stringify(),jquery的$.extend(true,{}, obj),lodash的_.cloneDeep和_.clone(value, true)。
例1:
var a = {c: {d: 1}};
var b = $.extend(true, {}, a);
console.log(a === b); // 输出false
a.c.d = 3;
console.log(b.c.d); // 输出 1,没有改变。
例2:
<script>
// 深拷贝
var obj1 = {
name: 'zs',
age: 18,
sex: '男',
dog: {
name: '金毛',
age: 2
},
friends: ['ls', 'ww']
}
// 深拷贝 把o1的成员拷贝给o2
function deepCopy(o1, o2) {
for (var key in o1) {
// 获取key属性对应的值
var item = o1[key];
// 如果item 是对象?
// var o = {}
if (item instanceof Object) {
// var o = {};
o2[key] = {};
deepCopy(item, o2[key]);
} else if (item instanceof Array) {
// 如果item 是数组呢?
// var arr = [];
o2[key] = [];
//使用了递归
deepCopy(item, o2[key]);
} else {
// 如果是简单类型
o2[key] = o1[key];
}
}
}
var obj2 = {};
deepCopy(obj1, obj2);
// 修改obj1中的成员 是否会影响obj2?
obj1.dog.name = 'xxx';
obj1.friends[0] = 'xxx';
console.dir(obj2);
</script>
演示JS的原型继承
例:
<script>
// 继承:类型和类型之间的关系
// 学生类型 老师类型 -> Person类型
// 继承目的: 把子类型中共同的成员提取到父类型中,代码重用
// 父类型
function Person() {
this.name = 'zs';
this.age = 18;
this.sex = '男';
}
// 子类型
function Student() {
this.score = 100;
}
Student.prototype = new Person();
Student.prototype.constructor = Student;//必须指定闭包为子类型
var s1 = new Student();
console.log(s1.constructor);
console.dir(s1);
function Teacher() {
this.salary = 3000;
}
// 原型继承: 无法设置构造函数的参数
</script>
例2:
**
利用 call改变this对象的指向来实现继承构造函数的传参
**
<script>
// 借用构造函数
// 父类型
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
// this.sayHi
}
Person.prototype.sayHi = function () {
console.log(this.name);
}
// 子类型
function Student(name, age, sex, score) {
Person.call(this, name, age, sex);
this.score = score;
}
var s1 = new Student('zs', 18, '男', 100);
console.dir(s1);
</script>
组合继承
<script>
// 组合继承:借用构造函数 + 原型继承
// 父类型
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
Person.prototype.sayHi = function () {
console.log('大家好,我是' + this.name);
}
// 子类型
function Student(name, age, sex, score) {
// 借用构造函数
Person.call(this, name, age, sex);
this.score = score;
}
// 通过原型,让子类型,继承父类型中的方法
Student.prototype = new Person();
Student.prototype.constructor = Student;
// 学生特有的方法
Student.prototype.exam = function () {
console.log('考试');
}
// var s1 = new Student('zs', 18, '男', 100);
// console.dir(s1);
// var p1 = new Person('ls', 18, '男');
// console.dir(p1);
function Teacher(name, age, sex, salary) {
// 借用构造函数
Person.call(this, name, age, sex);
this.salary = salary;
}
// 通过原型让子类型继承父类型中的方法
Teacher.prototype = new Person();
Teacher.prototype.constructor = Teacher;
var t1 = new Teacher('ww', 30, '男', 100000);
console.dir(t1);
t1.sayHi();
</script>
面向对象的特性:封装、继承、多态
javascript 对照java的面向对象只能说是为了面向对象的思想而强有。