js进阶一:对象,函数

对象

内建对象

- 内建对象指由ES标准定义的对象,任何对ES标准的实现都可以使用这些对象,不用新建可直接调用对象

比如:Math String Number Boolean RegExp

Math.Pi;
String(200);

宿主对象

- 宿主对象指由JS运行环境为我们提供的对象,目前对于我们来说就是指浏览器为我们提供的对象
  • 比如BOM对象 和 DOM对象
  • window document console 。。。。。。

自定义对象,由自己定义的对象

创建对象

var obj = new Object();

var obj = {}

var obj2 = {
    name:"孙悟空",
    age:18, 
    address:"花果山",
    //对于特殊的属性名,可以通过双引号引起来
    "123":"hello",                   
    test:{  name:"猪八戒",
        age:28, 
        address:"高老庄"
    }
};

工厂模式创建对象

function createPerson(name , age , gender , address){
            
//创建一个对象
var obj = new Object();
                
//向对象中添加属性
obj.name = name;
obj.age = age;
obj.gender = gender;
obj.address = address;
obj.sayName = function(){       console.log("大家好,我是:"+this.name);
            };
return obj;
}

对象属性操作

属性值可以是任意的数据类型(String Number Boolean Null Undefined)也可以一个对象

添加/修改属性

对象.属性名 = 属性值 , 易用, 但有时不能用
obj.name = "孙悟空";
obj.age = 18;
obj.gender = "男";
对象["属性名"] = 属性值 , 难写, 但更通用
obj["123abc"] = "你好";
var str = "var";
console.log(obj[str]);

删除属性

delete 对象.属性名
delete obj.name;

查看属性

对象.属性名
var gender = obj.gender;
对象["属性名"]
obj["123abc"]

检查对象中是否含有某个属性

"属性名" in 对象
"name" in obj;

对obj中的属性进行枚举

for(变量 in 对象){}

通过这个方法对获取到未知属性名,并获取到属性值,
返回属性名给n 返回的属性名是String类型

for(var n in obj){
    console.log(n + " : " +obj[n]);
}

对象方法

当一个对象的属性是一个函数时,我们称这个函数是这个对象的方法

obj.sayName = function(){
                console.log("Hello 大家好 我是孙悟空~~~");
            };

代码块

{ }

JS中的代码块仅有分组的作用没有其他的作用,代码块中的内容对于代码块外是可见的


函数

函数也是一个对象,具有Object的所有的功能

  • 不同是的函数对象可以保存一些可执行的代码,并且在需要的时候可以调用这些代码

创建

方式一

function 函数名字(形参1,形参2,...形参n){
语句...
}

function sum(a , b ,c){
        
}
方式二

var 变量 = function(形参1,形参2,...形参n){
语句...
}

var fun2 = function(){
            alert("我是又一个函数");
        };
函数不写return 默认return undifined

  • JS函数在调用时,解析器(浏览器)不会去检查形参的类型和个数

  • 也可以传递任意数量的实参

  • 如果实参的数量小于形参,则没有对应实参的形参将会使undefined

  • 如果实参的数量大于形参,则多余的实参不会使用

  • 可以传递任意类型的实参

  • 实参可以是任意的数据类型,也可以是一个对象或函数

调用函数

方法一
函数名字(形参1,形参2,...形参n);
方法二
call() / apply()
var obj2 = {                        
            name : "沙和尚",
            sayName : function(){
                console.log(this.name); 
            }                               
        };          
        name = "window中的name";
        obj2.sayName.call(window);
  • 当调用函数对象的call和apply时都会导致函数立即执行,就相当于调用了函数一样

  • 如果通过call和apply去调用一个函数,则call和apply中的第一个参数将会是函数中的this,之后的参数是传入方法中的参数

  • call的参数,需要一个一个列出来

      fun2.call(obj,1,2);
    
  • apply的参数,需要封装为一个数组传

      fun2.apply(obj , [1 , 2]);
    

arguments

  • arguments是一个类数组对象,在它里边保存着函数执行时的实参

  • 函数的所有的实参都在arguments中保存,

  • 通过arguments即使不定义形参也可以使用获取到传入的实参

      function fun(a,b,c){
      Array.isArray() 可以用来检查一个对象是否是一个数组
      console.log(Array.isArray(arguments));
    
      //获取实参的数量
      console.log(arguments.length);
    
      //获取指定的实参
      console.log(arguments[2]);
    
      //callee代表的是当前正在调用的函数对象
      console.log(arguments.callee == fun);
      }
    

回调函数

你定义的,你没有调用,但最终它执行了: 发生了对应了事件或特定时刻(如点击事件)

自调函数

(function(){
})();

构造函数

构造函数就是普通的函数, 构造函数首字母大写
function Person(name , age){
            //通过this,向新对象中添加属性
            this.name = name;
            this.age = age;
            this.sayName = function(){
                console.log(this.name);
            };
        }
创建对象
var dog = new Person();
执行流程
创建一个新的对象
将新创建的对象设置为函数中的this
逐行执行代码
将新建的对象作为返回值返回

prototype原型对象(祖先对象)

  • 我们每次创建一个函数浏览器都会为函数添加一个属性叫做prototype,这个属性对应的是一个对象
    这个对象就是我们说的原型对象。

  • 如果以仅仅以函数的形式去调用函数,则原型对象没有任何作用

  • 当以构造函数的形式调用函数时,它所创建的对象中都会有一个隐含的属性

  • 我们可以通过"_ proto_"属性来访问这个对象

属性

constructor

  • 所有的同一类型的对象他们都共享同一个原型对象,这个原型对象就相当于一个公共的区域

  • 当我们去调用一个对象的属性或方法时,它会先去对象自身中寻找,如果找到了则直接使用,如果没找到则去原型对象中寻找,如果原型中有则返回原型中的值如果原型中没有,则去原型的原型中寻找,找到了则直接使用依次类推。

  • 我们可以将对象中共有的属性或方法统一添加到原型中,这样我们不用添加多个重复的属性或方法,也不会污染全局作用域

注意:Object的原型的原型为null,所以会一直找Object的原型,
如果他里面依然没有,则返回undefined

function MyClass(){
            
        }
        
//向函数的原型对象中添加一个属性
MyClass.prototype.hello = "你好";
        
MyClass.prototype.fun = function(){
alert("我是原型中的fun");
    };
        
//创建MyClass的实例
var mc = new MyClass();
var mc2 = new MyClass();
var mc3 = new MyClass();
        
mc.hello = "mc的你好";
console.log(mc3.__proto__ == MyClass.prototype)
console.log(mc2.hello);
判断方法是否属于对象
per.hasOwnProperty("toString")
显示原型/隐式原型
  • 每个函数function都有一个prototype,即显式原型
    创建函数的时候自动指向一个空的原型对象

  • 每个实例对象都有一个proto,可称为隐式原型
    创建函数对象的时候自动添加的,默认值为函数的prototype值

  • 对象的隐式原型的值为其对应构造函数的显式原型的值

  • ES6之前不可以直接操作隐士原型的值,可以直接操作显示原型

  • 实例对象的隐式原型为构造函数的显示原型的值

原型链(隐式原型链)

查找一个对象的属性时,先在基本属性中查找,如果没有,再沿着proto这条链向上找

原型属性问题
  • 读取对象的属性值时, 会自动到原型对象中查找
  • 设置对象的属性值时, 不会查找原型链, 如果当前对象中没有此属性,直接添加此属性并设置其值

对象继承

原型链继承

Student.prototype = new Person()

借用构造函数继承(使用call()方法)

function Person(name, age) {
    this.name = name
    this.age = age
}
function Student(name, age, price) {
    Person.call(this, name, age)
    this.price = price
}

组合继承(即使用构造函数继承,又使用原型链继承)

this

每次调用函数时,浏览器都会将一个对象作为隐藏的参数传递进函数,这个对象就是函数执行的 上下文对象,我们可以通过this来引用该对象

  • 本质上任何函数在执行时都是通过某个对象调用的

  • this就代表调用函数的当前对象

  • 在定义函数时, this还没有确定, 只有在执行时才确定

  • 当调用函数时没有明确指定当前对象,this就是全局对象window

      var obj3 = {            
          name:"沙和尚",
          age:38,
          sayName:function(){
                      function test(){
                          console.log(this.name);
                  }                   
          test();
              }
      };
                  
      obj3.sayName();
    

函数调用方式不同,this的值也不同

  • 以方法形式调用,谁调用this就是谁

  • 以函数形式调用,this永远是window

  • 构造函数的形式调用,this就是新创建的对象

  • 通过call和apply调用时,第一个参数会成为this,可以让一个函数在任意对象上调用

  • 通过事件绑定中的this就是绑定事件的元素(谁绑定的this就是谁)


作用域

全局作用域

  • 所有直接在script标签中编写的代码都在全局作用域中

  • 全局作用域在打开网页时创建,在网页关闭时销毁

  • 全局作用域中有一个全局对象window,window代表的是浏览器的窗口

  • 在全局作用域中创建的变量都会作为window对象的属性保存

  • 在全局作用域中创建的函数都会作为window对象的方法保存

  • 在全局作用域中创建的变量都是全局变量,可以在页面的任意位置访问

函数作用域

  • 函数作用域可以理解为是全局中的小的作用域

  • 函数作用域在函数调用时创建,在调用结束时销毁,每调用一次函数就会创建一个新的函数作用域

  • 在函数作用域中可以访问到全局变量,而在全局中无法访问到函数作用域中的变量,
    在函数中创建的变量如果不写var,则会变成全局变量

  • 当我们在函数中使用一个变量时,它会先在自身的作用域中寻找,如果有就直接使用,如果没有则去上一级作用域中寻找,找到则使用,没找到则继续寻找,直到找到全局作用域为止如果全局作用域中依然没有,则报错ReferenceError

  • 在函数作用域中也适用变量和函数的声明提前

  • 如果在函数作用域中想访问全局变量可以通过window对象来访问

      console.log(window.a);
    

块作用域

ES6的时候才有

作用域链

静态的,在编写代码的时候就确定了变量调用位置

变量声明提前

  • 使用var关键字声明的变量,都会在所有的代码执行之前被声明,但是不会赋值
    赋值会直到执行到赋值的代码时才执行

  • 如果不使用var关键字声明变量,则不会有声明提前的特性

  • 如果不写var,直接为变量赋值,则相当于为window对象添加属性

函数声明提前

  • 使用函数声明创建的函数,会在所有的代码执行之前创建,
    所以我们可以在函数声明前就去调用函数

      function fun(){
              console.log("我是fun函数。。。。");
          }
    
  • 使用函数表达式创建的函数,没有声明提前特性,所以不能在它创建之前调用

      var name = function fun(){
              console.log("我是fun函数。。。。");
          }
    

堆栈

基本数据类型

  • 基本数据类型的值是直接保存到栈内存中的

  • 基本数据类型的值都是互相独立的,修改一个不会影响到其他的值

  • 对基本数据类型相等比较时,比较的是数据的值

引用数据类型

  • 引用数据类型对象是保存在堆内存中的,而在变量中保存的是对象的引用(内存地址)

  • 我们是通过引用来操作对象的,当修改一个对象时有可能影响到其他的变量

  • 两个引用数据类型进行比较时,比较的是对象的地址,如果两个对象一模一样,但是地址不同也会返回false


闭包

  • 当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时, 就产生了闭包
闭包到底是什么?
  • 使用chrome调试查看
  • 理解一: 闭包是嵌套的内部函数(绝大部分人)
  • 理解二: 包含被引用变量(函数)的对象(极少数人)
  • 注意: 闭包存在于嵌套的内部函数中
闭包的作用
  • 使函数内部的变量在函数执行完后, 仍然存活在内存中(其它变量已释放)

  • 让函数外部可以读取到函数内部的数据(变量/函数)

闭包的应用
  • 回调函数(定时执行函数)

  • 模块化

      function fun(n,o) {
      console.log(o)
      return {
      fun:function (m) {
          return fun(m,n)
          }
      }
      }
    
      console.log(fun(0).fun(1).fun(2))
    
      //  另一个
      function f1(){
          var n=999;
          nAdd=function(){n+=1}
          function f2(){
            alert(n);
          }
          return f2;
        }
        var result=f1();
        result(); // 999      
        nAdd();       
        result(); // 1000
    
  • 将所有的数据和功能都封装在一个函数内部(私有的)只向外暴露一个对象(函数), 并给对象添加一些公开的方法

  • 模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能

闭包的缺点及解决
1. 缺点
  • 函数执行完后, 被引用的变量没有释放, 占用内存时间会变长

  • 容易造成内存泄露

2. 解决
  • 能不用闭包就不用

  • 及时释放

你可能感兴趣的:(js进阶一:对象,函数)