在写这边之前小编先来吐槽一下JavaScript,看了好多都在讲想要学好JavaScript面向对象必须摒弃一些其他高级语言如 Java、C# 等类式面向对象思维的干扰,全面地从函数式语言的角度理解 JavaScript 原型式面向对象的特点。(听到这小编的心里凉了好多,学个Java、C++容易吗?)因为一个是基于类的(class-based)面向对象和 基于原型的 (prototype-based) 面向对象。
面向对象概述
面向对象思想小编理解为 将同一类事物进行抽象共有属性、方法成一个模板,也就是我们所说的类,而 对象则是类的实例化。
当然提到面向对象自然就少不了面向对象的三大特征,接触过 C++,Java这类的语言自然对面向对象有所了解,这里小编就不废话了。下面言归正传,来总结JavaScript中的面向对象,由于是初学,写的有误的地方还请给我留言指出来。
自定义对象
一、对象的定义
JavaScript 通过 <mark>{}</mark> 来实现的,成员以键值对的形式存放,每个成员之间用 <mark>,</mark> 隔开
var obj = {
name:'Smith',
age:18
};
注意:
{} 语法又名 对象的字面量语法,即在源代码中直接书写一个表示数据和类型的量
JSON 和这个差不多,只不过是用 "" 来包裹 对象的字段和字符串值
var obj = {
"name":'Smith',
"age":18
};
二、访问对象成员
创建对象后通过 <mark>.</mark> 访问对象成员
- 静态属性定义
var obj = {};
obj.name = "Smith";
- 动态属性定义
var obj = {};
var key = 'name';
obj[key] = "Smith"; // 这种好处就是可以在代码执行时动态给对象添加成员
三、对象的遍历
<mark>for…in</mark> 不仅可以遍历元素,也可以遍历对象成员,但是 <mark>for…of</mark>不可以,切记!!!
var p = {
name:'wang',
age:12
}
for (var k of p) {
console.log(k +'-'+p[k]);// 我试了for...of,报错 p is not iterable,说明不具有迭代功能
}
四、判断对象是否存在
var p = {
name:'wang',
age:12
}
console.log(name in p); // true
console.log(id in p); // false
五、深拷贝与浅拷贝
- 浅拷贝:相当于使两个数组指针指向相同的地址,任一个数组元素发生改变,影响另一个。
- 深拷贝:两数组指针指向不同的地址,数组元素发生改变时不会相互影响。
这里我提一下小编自己觉得特别重要的点
1、将一个基本数据类型(数值、字符串)的变量赋值给另一个变量,此时得到两个相同的变量,改变一个变量时,另一个不会变化
2、操作目标是引用数据类型(数组、对象),还是采取赋值的话,一个变了,两个都会发生变化(这里就涉及到指针问题了,
两个指针指向同一块内存)
这里我就不想提了,学过C++的应该都知道,小编觉得和 C++ 中特别相似,想了解的可以参见 这里
构造函数
一、创建构造函数
构造函数是JavaScript创建对象的另外一种方式,相比于 字面量 <mark>{}</mark> 的方式,这种方式更能创建出一些具有 <mark>相同特征的对象</mark>。
- 工厂函数:表面是个函数,其内部还是采取 字面量的方式创建对象
// 创建一个 类模板 function Person(name,age){ var obj = {}; obj.name = name; obj.age = age; return obj; } // 实例化对象 var li = Person('li',20);
- JavaScript内置构造函数
- 自定义构造函数,这种跟Java C++的思维很像。注意:在构造函数内部<mark>this</mark>表示刚刚创建的对象
ES6 中新增了 class关键字,来定义一个类,这种是小编觉得最符合 Java、c++的编程风格,但不幸的是为了避免用户的浏览器不支持,所以慎用(小编很难过)// 创建一个 类模板 function Person(name,age){ this.name = name; this.age = age; this.sayHello = function(){ console.log('Hello,my name is ' + this.name); } } // 实例化对象 var li = Person('li',20); li.sayHello();
class Person{ constructor(name,age){ this.name = name; this.age = age; } sayHello(){ console.log('Hello,my name is ' + this.name); } } // 实例化一个对象 var person = new Person('li',20); person.sayHello();
二、私有属性
var 关键字来定义变量 视为 私有属性,其余的参考<mark>Java的实体类封装</mark>即可,不再浪费时间
下面介绍一下小编觉得特特重要的东西:构造函数中的<mark>return</mark>关键字
1、 return 返回一个数组 或 对象等引用类型数据,则构造函数会直接返回该数据,不会返回原来创建的对象
2、 return 返回的是基本类型数据,则返回的数据无效,依然返回原来创建的对象
// return 返回一个数组 或 对象等引用类型数据,则构造函数会直接返回该数据,不会返回原来创建的对象
function Person(){
obj = this;
return 123;
}
var obj,p = new Person();
console.log(p === obj); // true
// return 返回的是基本类型数据,则返回的数据无效,依然返回原来创建的对象
function Person(){
obj = this;
return {};
}
var obj,p = new Person();
console.log(p === obj); // false
三、函数中的this 指向
-
分析 this 指向
1、使用 new 关键字将函数作为构造函数调用时,构造函数内部的this 指向 新创建的对象 2、直接通过函数名调用,this 指向的是全局对象 (在浏览器中表示 window对象) 3、如果将函数作为对象的方法调用,this 将会指向该对象
function fn(){ return this; } var obj = { name:'li', func:fn } console.log(fn() === window); // true console.log(obj.func() === obj); // true
-
更改 this 指向
除了遵循默认的 this 指向规则,函数调用者还可以用下面方法手动控制 this 指向。- apply()
- call()
function method(){ console.log(this.name); } method.apply({name:'张三'}); // 张三 method.call({name:'李四'}); // 李四
下面小编来解读这段代码
相同点: apply() 和call() 方法都能更改函数内的 this 指向,这两个方法的第一个参数 代表 this 指向的对象。 换句话说,this指向 第一个参数的对象 不同点: 1、apply()第二个参数表示调用函数时传入的参数, 用 数组 的形式传递 2、call() 使用 2~N 个参数来表示调用函数时传入的函数 即 ,传参方式不同(应该可以这么说吧,哈哈哈)
下面举个栗子:
function method(a,b){ console.log(a,b); } method.apply({},[1,2]); // 12 method.call({},3,4); // 34
- ES5 新增方法 bind() ,绑定的意思
在调用函数前指定 this 的含义,实现提前绑定的效果。在绑定时,还可以提前传入调用函数时的参数
function method(a,b){ console.log(this.name + a + b); } var name = 'zhangsan'; var test = method.bind({ name:'lisi' },3,4); method(1,2); // zhangsan12 test(); // lisi34