所不知道的JS

创建函数对象的方式 & 函数vs方法

1. 使用构造函数的方式(不使用)
var fun = new Function("console.log('Hello')") //创建一个函数对象(可以将要封装的代码以字符串的形式传递给构造函数) 
2. 使用函数声明来创建一个函数
function 函数名([形参1,形参2,...形参n]){
    console.log("Hello")
}
3. 使用函数表达式来创建一个函数(匿名函数赋值给一个变量)
var 函数名 = function([形参1,形参2,...形参n]){
    console.log("Hello")
}

注意:
    对象的属性值可以是任何的数据类型,也可以是一个函数
    obj.sayName = function(){
        .....
    } (写法1)
    写法2:
    var obj = {
        name:"猪八戒",
        age:18,
        sayName:function(){
            ....
        }
    };
    obj.sayName();
    如果一个函数作为一个对象的属性保存,则
    称这个函数是这个对象的方法
    调用这个函数 -> 调用对象的方法(method)

    但它只是名称上的差异,本质上无区别

立即执行函数

1. 引入:
        function(){
            alert("我是一个匿名函数");
        }
        如果单纯这么写,则会报错,因为js会把{...}当成一个代码块,然后不认识前面的function()
        可以给这个函数加上一个括号(),表示这整个匿名函数是个整体
        (function(){
            alert("我是一个匿名对象");
        })
        如何调用这个匿名函数呢,调用函数一般格式是 函数对象() 
        这里上面这种即为函数对象,所以调用形式为:
        (function(){
            alert("我是一个匿名对象");
        })();
        该种函数称为“立即执行函数”

2. 格式:
    1) 无参
        (function(){
            alert("我是一个匿名对象");
        })();

    2) 有参
        (function(a, b){
            console.log("a = " + a);
            console.log("b = " + b);
        })(123,234);


3. 特点:
4) 函数定义完,立即被调用
5) 往往只会执行一次

call() 和 apply()


- 这两个方法都是函数对象的方法,需要通过函数对象去调用
    > 函数对象 vs 函数
    >    : function fun(){
    >            alert("xxxx");
    >        }
    >        fun(); //函数
    >        fun //函数对象
- 当对函数对象调用call()apply() 方法,都会执行函数
    如: fun.call() 
        fun.apply()   
        fun();
- 当调用call和apply() 可以将一个对象指定为第一个参数
  此时,这个对象将会称为函数执行时的this: var obj = {};
        function fun() {
            alert(this);
        }
        fun.call(obj); //Object
        fun.apply(obj); //Object
        fun(); //Window
- call()方法可以将实参在对象之后一次传递
        function fun2(a,b){
            alert(a);
            alert(b);
        }
        fun2.call(obj); //undefined undefined
        fun2.call(obj,2,3); //2 3
- apply()方法需要将实参封装到一个数组中统一传递
        fun2.apply(obj,[2,3]); //2 3
    

this


    function fun(){ //隐藏了一个名为this的形参
        console.log(this);
    }
    var obj = {
        sayName:fun;
    }
    解析器在调用函数时,(浏览器)每次都会向函数内部传递一个隐含的参数this(this指向的是一个对象,这个对象称为“函数执行的上下文对象”)
    根据函数的调用方式的不同,this会指向不同的对象
        1)以函数的形式调用时,this永远都是window
            fun();      //window
        2)以方法的形式调用时,this就是调用方法的那个对象
            obj.sayName();     //object,obj
        3)以构造函数的形式调用时,this就是新创建的那个对象
            new Dog();
        4) 使用call和apply调用时,this是指定的那个对象

arguments

在调用函数时,浏览器每次都会传递进两个隐含的参数:
1. 函数的上下文对象 this
2. 封装实参的对象 arguments
    - arguments:一个类数组对象,它也可以通过索引来操作数据,也可以获取长度
        function fun() {
            //console.log(arguments instanceof Array); //false
            //console.log(Array.isArray(arguments)); //false
            console.log(arguments.length);
        }
    - 在调用函数时,我们所传递的实参都会在arguments中保存
    - arguments.length可以用来获取实参的长度
        fun("hello"); //1
        fun("hello",true); //2
        function fun() {
            console.log(arguments[0]);
        }
        fun("hello",true); //hello

    - 即使不定义形参,也可以通过arguments来使用实参
      只不过比较麻烦(定义了实参,也可以)
            arguments[0] 表示第一个实参
            arguments[1] 表示第二个实参
        
    - 它里面有一个属性 callee,对应一个函数对象,就是当前正在执行的函数对象
        function fun(a,b) {
            console.log(arguments.callee);
        }

        fun("hello", true); //function fun(a,b) {.....}

使用工厂方法创建对象——大批量创建对象

function createPerson(name,age,gender) {
    var obj = new Object();  

    //向对象添加属性
    obj.name = name;
    obj.age= age;
    obj.gender= gender;

    //返回新的对象
    return obj;
}

var obj2 = createPerson("xx",28,"男");

function createDog(name,age,gender){
    var obj = new Object();  //使用的构造函数都是Object,创建的对象都是Object这个类型,无法具体区分出多种不同类型的对象
    ... ...
}

为了解决上述的问题,引入构造函数

使用构造函数创建对象

创建一个构造函数,专门用来创建Person对象的

构造函数 vs 普通函数
相同点:构造函数就是一个普通的函数,创建方式和普通函数没有区别
区别:
    1) 构造函数习惯上首字母大写
    2) 调用方式不同:
        普通函数直接调用
        构造函数:使用new关键字来调用

构造函数的执行流程:
    1)立刻创建一个新的对象
    2)将新建的对象设置为函数中的this,在构造函数中可以使用this来引用新建的对象
    3)逐行执行函数内的代码
    4)将新建的对象作为返回值返回

使用同一个构造函数创建的对象,称为一类对象,也将一个构造函数称为一个类。
将通过同一个构造函数创建的对象,称为是该类的实例。


function Person(){
    this.name = name;
    this.age = age;
    this.sex = sex;
    this.sayName = function(){
        alert(this.name);
    }
}

function Dog(){

}

var per = new Person("xxx",18,"male");
var per2 = new Person("xxx2",19,"female");

var dog = new Dog();

//使用instanceof可以检查一个对象是否是一个类的实例
console.log(per instanceof Person); //true
console.log(dog instanceof Person); //false

//所有的对象都是Object的后代
console.log(dog instanceof Object); //true
console.log(per instanceof Object); //true

问题 & 解决方式:原型

function Person(name, age, gender){
    this.name = name;
    this.age = age;
    this.gender = gender;
    //向对象中添加一个方法
    this.sayName = function(){
        alert("Hello,I am " + this.name);
    }
}
引入问题:sayName方法定义在构造方法中,每次创建一个对象(执行一次构造函数),就会创建一个新的sayName方法(内存中开辟一个空间,存放这个方法),则:所有实例的sayName都是唯一的,且方法都是一样的,浪费空间,完全可以用让所有的实例对象共享同一个方法

解决方式1:将sayName方法定义在全局作用域中
function fun() {
    alert("Hello,I am " + this.name);
};

function Person(name, age, gender){
    this.name = name;
    this.age = age;
    this.gender = gender;
    //向对象中添加一个方法
    this.sayName = fun;
}
局限(方式1):
1) 将函数定义在全局作用域中,污染了全局作用域的命名空间(名字不可重复)
2) 定义在全局作用域中很不安全(开发中往往是团队开发,很可能重名覆盖)
    
解决方式2(改进方式1):原型prototype
我们所创建的每一个函数,解析器(浏览器)都会向函数中添加一个属性prototype(这是属性对应一个对象-原型对象)
每个函数的prototype都是唯一的

如果函数作为普通函数,调用prototype没有任何作用
当函数以构造函数的形式调用时(new Dog()),它所创建的对象(实例对象)中都会有一个隐含的属性  指向该构造函数的原型对象,我们可以使用__proto__来访问该属性

原型对象就相当于一个公共的取域,同一个类的所有实例都可以访问到这个原型对象
我们可以将对象中共有的内容,统一设置到原型对象中

当我们访问对象的一个属性或方法时,会现在对象自身中寻找,如果有则直接使用;
若没有,则到原型对象中寻找

创建构造函数时,可以将这些对象共有的属性和方法,统一添加到构造函数的原型对象中,
这样不用分别为每一个对象添加,也不会影响到全局作用域,就可以使每个独享都具有这些属性和方法了

function MyClass(){

}
//向MyClass的原型中添加属性a
MyClass.prototype.a = 123;

//向Person的原型中添加一个方法
Person.prototype.sayHello = function(){
    alert("Hello,I am " + this.name);
}

var mc = new MyClass();
var mc2 = new MyClass();

console.log(MyClass.prototype);
console.log(mc2.__proto__ == MyClass.prototype); //true

//向mc中添加a属性
mc.a = "我是mc中的a";

console.log(mc.a); //我是mc中的a
console.log(mc2.a);  //123

//使用in检查对象中是否含有某个属性时,如果对象中没有,但是原型中有,也会返回true
console.log("a" in mc2); //true

//对象的hasOwnProperty()检查对象自身中是否含有该属性
console.log(mc2.hasOwnProperty("a")); //false

console.log(mc2.hasOwnProperty("hasOwnProperty")); //false

console.log(mc2.__proto__.hasOwnProperty("hasOwnProperty")); //false
//mc2的原型对象也没有,原型对象是对象,所以它也有原型,则也有__proto__属性
/* 当我们使用一个对象的属性或方法时,
        1) 自身中有,则直接使用
        2) 如果自身没有,则去原型对象中寻找,如果原型对象中有,则使用
        3) 如果原型对象中也没有,则去原型的原型中寻找,直到找到Object对象,则停止(如果在Object中依然没有找到,则返回undefined)
*/


console.log(mc2.__proto__.__proto__);
console.log(mc2.__proto__.__proto__.hasOwnProperty("hasOwnProperty")); //true
console.log(mc2.__proto__.__proto__.__proto__); //null

在这里插入图片描述

垃圾回收(GC)

- 就像人生活的时间长了会产生垃圾一样,程序在运行过程中也会产生垃圾
  这些垃圾积攒过多以后,会导致程序运行的速度过慢
- 当一个对象没有任何的变量或属性对它进行引用,此时我们将永远无法操做该对象
  此时,这种对象就是一个垃圾,这种对象过多占用大量的内存空间,导致程序运行变慢,
  所以这种垃圾必须进行清理

        var obj = new Object();
        //对对象进行各种操作
        ... ...

        obj = null;
-JS中拥有自动的垃圾回收机制,会自动将这些垃圾对象从内存中销毁,
  我们不需要也不能进行垃圾回收的操作
- 需要做的只是将不再使用的对象设置null即可

字面量 vs 变量

1. 字面量:一些不可改变的值
        1):1 2 3 4 5   alert(12323232323)中的12323232323
        2)字面量都是可以直接使用,但是我们一般都不会直接使用字面量

2. 变量:变量可以用来保存字面量,而且变量的值是可以任意改变的
        变量更加方便使用,所以在开发中都是使用变量去保存一个字面量
        而很少直接使用字面量
        1) 声明:在js中使用var关键字来声明一个变量,: 
            
            var a;  //声明变量
            a = 123;  //为变量赋值
            a = 456;

3. 数据类型指的就是字面量的类型
   在JS中,一共有六种数据类型
        基本数据类型:
            String 字符串
            Number 数值
            Boolean 布尔值
            Null 空值
            Undefined 未定义
        引用数据类型:
            Object对象

Null 和 Undefined

1.Null(空值) 类型的值只有一个,就是null
    1) null这个值专门用来表示一个为空的对象
    2) 使用 typeof 检查一个null值时,会返回object
        例: var a = null;
            console.log(typeof a); //object
2.Undefined(未定义)类型的值只有一个,就是undefined
    1) 当声明一个变量,但并不给变量赋值时,它的值就是undefined
        var b;
        console.log(b); //undefined
    2) 使用typeof 检查一个undefined值时,也会返回undefined: console.log(typeof b);   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值