在写这边之前小编先来吐槽一下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>表示刚刚创建的对象
    	// 创建一个 类模板
    	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();
    
    ES6 中新增了 class关键字,来定义一个类,这种是小编觉得最符合 Java、c++的编程风格,但不幸的是为了避免用户的浏览器不支持,所以慎用(小编很难过)
    	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 指向。

    1. apply()
    2. 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