声明函数方式
- 声明函数
function functionName(parameters) {
执行的代码
}
函数声明后不会立即执行,只是在初始化的时候会将函数声明提升,会在我们需要的时候调用到。
2、函数表达式(匿名函数)
var x = function (a, b) {return a * b};
var z = x(4, 3);
以上函数实际上是一个 匿名函数 (函数没有名称)。
函数存储在变量中,不需要函数名称,通常通过变量名来调用。
实例对象
function Person(name, age) {
this.name = name
this.age = age
this.eat = function(){
console.log('吃东西')
}
}
let p1 = new Person('小明',30)
let p2 = new Person('小红',30)
构造函数中,不止有属性,还有方法
实例对象:调用构造函数之后创建的对象,p1就是实例对象
console.log(p1.eat==p2.eat)
返回false
内存中栈与堆
简单数据类型,如数字、字符串、布尔、指针等类型,存放在栈中。
复杂数据类型,如数组,对象(函数)等存放在堆中
当new构造函数的时候,会在堆内存中new一个空对象,则this指向这个新对象的内存地址,如0x0001。值类型对象也是存在这个内存空间中的。p1中的function,还会在堆中额外的再开空间
举例:张三和李四在银行中都有100块钱,但是指向不同
每调用一次构造函数,就会执行function,在堆中开辟新的内存空间,虽然函数一样,但是堆地址不同。所以构造函数会导致内存浪费
通过全局函数,解决构造函数浪费内存空间
思路是把构造函数中的function写在外面,并赋给变量,里面赋值给该变量,即可
let fn = function () {
console.log('我要吃东西')
}
function Person(name, age) {
this.name = name
this.age = age
this.eat = fn
}
注意,这里的fn后面不要加(),不加代表取变量地址,加了代表传值,调用函数获取返回值了
此时再执行console.log(p1.eat==p2.eat)
返回true,两者指向的都是fn。fn变量名在栈中,是指针类型,指向function堆中的地址0x003。p1与p2的eat都是拷贝fn的地址赋值给eat
虽然解决的资源浪费,但会使内存中的全局变量太多,导致变量污染,使用function fn1(){}
方式声明,不会报错,会直接覆盖
使用对象解决函数的变量污染
let obj = {
fn1: function () {
console.log('我要吃东西')
},
fn2: function () {
cobsole.log('我要吃东西')
},
fn3: function () {
console.log('我要学习')
}
}
//构造函数
function Person(name, age) {
this.name = name
this.age = age
this.eat = obj.fn1
this.play = obj.fn2
this.learn = obj.fn3
}
obj自己成为全局变量,解决了其它全局变量的污染问题,但自己称为了污染
原型对象
原型对象,相当于上面代码中的obj,但更牛一些
js作者创造了原型对象,解决上面的问题。每个函数在创建出来的时候,都会创建出一个原型对象,它与obj的作用是一样的,但原型对象不是全局对象。它是函数的一个属性,叫prototype原型,默认指向一个空对象,
Person.prototype.eat = function () {
console.log('我要吃东西')
}
执行console.log(p1.eat==p2.eat)
返回true。
console(p1)的时候不能直接看到eat,但其实存在在prototype对象中
构造函数new看作是爹,原型对象prototype看作是妈,实例对象是孩子,实例对象可以无条件的使用爹妈的资源。只要创建一个函数,就送一个原型对象与之对应,因为只有爹,不能生下孩子,一夫一妻制。解决了资源浪费、变量污染,爹妈一起生了100个孩子,也永远只有1个eat原型对象方法在那里,每个孩子都可以无条件的访问