【第一章】灵活的语言——JavaScript

一、function的直接定义和存在变量里区别

平时我们定义函数有两种方式:

  1. 直接定义
  2. 定义一个变量,将函数赋值给这个变量

这两中定义方式有什么区别呢?

  1. 直接定义的函数,可以直接调用,因为存在变量提升,不需要提前声明。
  2. 函数变量在调用前需要提前声明

测试实例:

f_func(); //直接定义的函数无需提前声明
v_func(); //函数变量,未声明时调用失败

function f_func() {
	console.log("我是直接定义的函数f_func");
}

var v_func = function() {
	console.log("我是函数变量v_func");
}

v_func(); //声明后才可以调用

在这里插入图片描述
将第一个 v_func(); 注释掉之后,再运行一次。这次的运行结果正常了。
在这里插入图片描述
上面的运行结果可以看出,将函数存在变量里面,在调用前需要提前声明。

另外还有一点就是,在页面直接定义一个函数,相当于直接创建了一个全局变量
在这里插入图片描述

上面的定义方式从功能层次来说没有问题,但是在团队开发中不建议这样做。

首先是直接定义的方式,如果别人也定义了相同的方法,原功能就会被覆盖掉。

其次是函数变量的方法,减少了覆盖和被覆盖的可能性,当然一旦被覆盖,所有的功能将会失效,这种现象是很明显的,我们可以轻易察觉到。

解决方法

用对象收编变量

可以创建一个对象,将我们自己写的方法放进去。

var Test = {//属性:值
	func1:function(){
		console.log("我是func1");
	},
	func2:function(){
		console.log("我是func2");
	},
	func3:function(){
		console.log("我是func3");
	},
}
Test.func1();//使用点运算符调用对象里的方法

在这里插入图片描述
对象的属性是可以动态添加的,所以上面的定义方式有另一种形式。

var Test = function() {};
//动态给对象添加属性
Test.func1 = function() {
	console.log("我是func1");
}
Test.func2 = function() {
	console.log("我是func2");
}
Test.func3 = function() {
	console.log("我是func3");
}
Test.func3(); //使用点运算符调用对象里的方法

在这里插入图片描述
上面这种方式,只创建了一个全局对象,而且有效的解决了方法覆盖和被覆盖的问题,但是随之而来的另一个问题就是,如果别人想用你对象里的方法,该怎么办呢?

首先这是一个对象,这个对象是不能复制一份的,或者说这个对象在用new关键字创建新的对象的时候,新创建的对象是不会继承这些方法的。

var Test = function() {}; 
Test.func1 = function() {
	console.log("我是func1");
}
Test.func2 = function() {
	console.log("我是func2");
}
console.log("对象:",Test);
var OtherTest = new Test();	
console.log("通过new创建新的对象:",OtherTest);
var AnotherTest = Test;
console.log("直接复制对象:",AnotherTest);

在这里插入图片描述
上图可以看出,我们添加进去的方法确实都没有继承到。那么,该怎么解决呢?

真假对象

函数对象

如果是简单的复制,可以将方法放在一个函数对象中,然后return出去。

var Test = function() {
	return {
		func1:function(){
			console.log("func1");
		},
		func2:function(){
			console.log("func2");
		}
	}
}

var A = Test();
A.func1();
var B = Test();
B.func2();

在这里插入图片描述
上面这种写法,每次别人调用这个函数的时候都返回了一个新的对象,明面上执行的是Test对象,实际上返回的是新对象,这样每个人使用的时候就互不影响了。

上面的方法,并不是一个真正意义上的类的创建,并且创建出来的A对象B对象和原始的Test对象一点关系都没有。所以我们可以改造一下,将对象改造成类。

var Test = function() {
	this.func1 = function (){
		console.log("func1");
	}
	this.func2 = function (){
		console.log("func2");
	}
}

console.log("瞅瞅类的样子:",Test);

var A = new Test();//类要使用new关键字
A.func1();
console.log("瞅瞅类的实例:",A);

在这里插入图片描述
我们把所有的方法放在了函数的内部,通过this定义的,所以每一次通过new关键字创建新对象的时候,新创建的对象都会对类的this上的属性进行复制。所以这些新创建的对象都会有一套属于自己的方法。

但是上面的这种操作,有时候造成的消耗是很大的,所以我们需要再进行优化一下。

创建一个Test类

var Test = function (){
	Test.prototype.func1 = function (){//将方法添加到原型上
		console.log("func1");
	}
	Test.prototype.func2 = function(){
		console.log("func2");
	}
}
var A = new Test();
A.func1();
console.log(A);

在这里插入图片描述
这里涉及到了原型,没有相关知识的可以先去了解一下再看,从上面的图可以看出,优化后new出来的实例,方法都在prototype上,也就是原型上。

这样做和直接写在this上有什么区别呢?想一下我们为什么要进行优化,内存,也就是说,这样优化后,造成的内存消耗没那么多。

那么为什么呢?用生活中的例子可能会更好理解:
this
我们把方法写在this上面,那么每次new一个实例的时候,就会复制一遍,相当于,我画了一本画册(类),里面有3幅画(方法),画上面记录了我去过的旅游景点(方法实现的功能),然后小明想要看看我第2幅画(调用方法),我就给他画了一本画册(创建实例),这个过程,我把那3幅画重新画了一次。小红来了想看第1幅画,我又画了一本画册,又把那3幅画重新画了一次。

画给这两个人两本画册,消耗纸张 2×3=6
prototype
把方法定义在原型上呢,就相当于,小明想看看我第1幅画,我直接把画册的第1幅画给他看,小红想看第2幅画,我就直接把画册的第2幅画给她看。

消耗纸张 0

上面两个过程,小明和小红都看到了自己想看的那幅画,但是对于“我”而言,消耗却是不一样的,很明显把方法写在原型上对我来说消耗更小。

原型的另一种写法

两种写法不能混着用,只能选择一种。

var Test = function() {}
Test.prototype = {
	func1: function() {
		console.log("func1");
	},
	func2: function() {
		console.log("func2");
	}
}

方法的另类用法(链式)

正常情况:

var Test = function() {}
Test.prototype = {
	func1: function() {
		console.log("func1");
	},1
	func2: function() {
		console.log("func2");
	}
}
var A = new Test();
A.func1();
A.func2();

看最后两行代码,我们写了两次对象A,这个操作时可以避免的,只需要在声明的每一个方法末尾处将当前对象(this)返回,在JS中this就是指向当前对象的。

另类用法:

var Test = function() {}
Test.prototype = {
	func1: function() {
		console.log("func1");
		return this;
	},
	func2: function() {
		console.log("func2");
		return this;
	}
}
var A = new Test();
A.func1().func2();

在这里插入图片描述
像上面最后一行代码的这种调用方式,称为 链式调用

函数的祖先

prototype.js
一款JS框架,最大的特点是对原生对象的拓展。

比如想个每个函数都添加一个 func3 方法,可以这样做:

Function.prototype.func3 = function (){
	console.log("func3");
}
//函数形式
var f = function () {}
f.func3();
//类形式
var m = new Function();
m.func3();


当然,上面是举个例子,一般不建议这样做,因为这样做污染了原生对象Function,进而别人创建的函数也会被污染,造成不必要的开销。

不过可以通过抽象出一个统一添加方法的功能方法。

//抽象方法
Function.prototype.addMethod = function(name,fn){
	this[name] = fn;
	return this;//这样就可以链式添加
}

var methods = Function();
methods.addMethod('func3',function(){
	console.log('func3');
}).addMethod('func4',function(){
	console.log('func4');
});
//函数式调用
methods.func3();
methods.func4();

对于习惯使用类式调用的开发者,需要把代码更改一下:

Function.prototype.addMethod = function(name,fn){
this.prototype[name] = fn;
	return this;//这样就可以链式添加
}

var methods = function(){};
methods.addMethod('func3',function(){
	console.log('func3');
}).addMethod('func4',function(){
	console.log('func4');
});

注意,这个写法不能直接调用函数了

methods.func3();

在这里插入图片描述
应该通过new关键字来创建一个新的实例对象,然后再调用方法。

var B = new method();
B.func3();

在这里插入图片描述

本章完。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值