资讯专栏INFORMATION COLUMN

Javascript重温OOP之类与对象

RancherLabs / 990人阅读

摘要:对构造函数使用运算符,就能生成实例,并且变量会绑定在实例对象上。这个对象的所有属性和方法,都会被构造函数的实例继承。

对象 对象的含义

所谓对象,就是一种无序的数据集合,由若干个“键值对”(key-value)构成。

对象的创建

使用new运算符创建Object

var p = new Object();
p.name = "Tony";    

使用对象字面量的形式

//对象字面量形式
var p ={
    name: "tony",
    work: function(){
        console.log("working....");
    },
    _age: 18,
    get age(){
        return this._age;
    },
    set age(val){
        if( val <0 || val > 150){
            throw new Error("invalid value");
        }else{
            this._age = val;
        }
    }
}
console.log(p.name);

对象的基本操作

成员属性的添加

// Object.defineProperty()方法
Object.defineProperty(p, "age",{value: 18, writable: false});
//Object.defineProperties()方法 添加多个属性
Object.defineProperties(p, {
    salary:{
        value: 1000,
        writable: false
    },
    gender:{
        value: true
    }
});

成员的遍历

使用 for..in语句

Object.keys()方法 返回一个包含对象键名的字符串数组

var o ={};
o.name = "jack";
o.age = 20;
for(var i in o){
    console.log(o[i]);
} // jack, 20
Object.keys(o); // ["name", "age"]

检查对象是否有某个属性

in 操作符

Object.hasOwnProperty()方法

var o = {name: "mariya"}
"name" in o; // true
o.hasOwnProperty("name"); // true

得到对象的属性特性描述

Object.getOwnPropertyDescriptor(obj,property)

Object.getOwnPropertyDescriptor(o, "name");
//Object {value: "mariya", writable: true, enumerable: true, configurable: true}

删除属性

delete运算符,但有些对象的属性是删除不了的

delete o.name; //true
o.name;  // undefined 

成员特性

configurable 是否可设置

writable 是否可修改

enumerable 是否可枚举

value属性的值

var person ={};
Object.defineProperties(person,{
    title: {value: "fe",enumerable: true},
    age: {value: 19, enumerable: true, writable: true}
});
Object.getOwnPropertyDescriptor(person,"title");
// Object {value: "fe", writable: false, enumerable: true, configurable: false}
Object.getOwnPropertyDescriptor(person,"age");
// Object {value: 19, writable: true, enumerable: true, configurable: false}
Constructor属性

constructor始终指向创建当前对象的构造函数。

var arr = [];
console.log(arr.constructor === Array); // true
var Foo = function() {};
console.log(Foo.constructor === Function); // true
// 由构造函数实例化一个obj对象
var obj = new Foo();
console.log(obj.constructor === Foo); // true
console.log(obj.constructor.constructor === Function); // true

每个函数都有一个默认的属性prototype,而这个prototypeconstructor默认指向这个函数。

function Person(name) {
    this.name = name;
};
Person.prototype.getName = function() {
    return this.name;
};
var p = new Person("jack");
console.log(p.constructor === Person);  // true
console.log(Person.prototype.constructor === Person); // true
console.log(p.constructor.prototype.constructor === Person); // true
类的创建

虽然js是门基于对象的语言,但是没有类这一概念的,虽然保留了class的关键字,但在ES6之前是无法使用的。所以,可以用构造函数模拟类的创建,也就是伪类。

所谓"构造函数",其实就是一个普通函数,但是内部使用了this变量。对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例对象上。

每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。

这意味着,我们可以把那些不变的属性和方法,直接定义在prototype对象上。

//构造函数模式
function Person(age, name){ //Class
    this.age = age;
    this.name = name;
}
//将公共的属性或方法放在prototype属性上
Person.prototype.headCount = 1;
//创建实例对象
var p = new Person(19, "johnsom");
var p1 = new Person(20, "allen");
this

this表示当前对象,如果在全局作用范围内使用this,则指代当前页面对象window; 如果在函数中使用this,则this指代什么是根据运行时此函数在什么对象上被调用。 我们还可以使用applycall两个全局方法来改变函数中this的具体指向。

1. 全局代码中的this
console.log(this === window); //true 全局范围内使用this指向window对象
2. 普通的函数调用
function f(){
    this.name = "tony"; // this在运行时指向window对象,在严格模式下则是undefined
}
3. 在对象中使用
var o = {
    name: "tony",
    print: function(){
        console.log(this.name);  //this指向对象o,但是可以改变其指向
    }
};
4. 作为构造函数
new F(); // 函数内部的this指向新创建的对象。
5. 多层嵌套的内部函数
var name = "global";
var person = {
    name : "person",
    hello : function(sth){
        var sayhello = function(sth) {
            console.log(this.name + " says " + sth);
        };
        sayhello(sth);
    }
}
person.hello("hello world");//global says hello world

在内部函数中,this没有按预想的绑定到外层函数对象上,而是绑定到了全局对象。这里普遍被认为是JavaScript语言的设计错误,因为没有人想让内部函数中的this指向全局对象。一般的处理方式是将this作为变量保存下来,一般约定为that或者self:

var name = "global";
var person = {
    name : "person",
    hello : function(sth){
        var that = this;
        var sayhello = function(sth) {
            console.log(that.name + " says " + sth);
        };
        sayhello(sth);
    }
}
person.hello("hello world");//person says hello world
6. 事件中的this
var ele = document.getElementById("id");
ele.addEventListener("click",function(){
    console.log(this);  //this指向dom元素
});
7. 使用apply和call改变this的指向

apply和call类似,只是后面的参数是通过一个数组传入,而不是分开传入。两者都是将某个函数绑定到某个具体对象上使用,自然此时的this会被显式的设置为第一个参数。两者的方法定义:

call( thisArg [,arg1,arg2,… ] );  // 参数列表,arg1,arg2,...
apply(thisArg [,argArray] );     // 参数数组,argArray
var name = "global";
var o = {
    name: "job",
    getName: function(){
        console.log(this.name);
    }
};
o.getName(); // job

//用call或apply改变函数中this的指向
o.getName.call(this); // global

简单的总结:

当函数作为对象的方法调用时,this指向该对象。

当函数作为淡出函数调用时,this指向全局对象(严格模式时,为undefined)

构造函数中的this指向新创建的对象

嵌套函数中的this不会继承上层函数的this,如果需要,可以用一个变量保存上层函数的this。

8. bind()

一个问题:

$("#ele").click = obj.handler;

如果在handler中用了this,this会绑定在obj上么?显然不是,赋值以后,函数是在回调中执行的,this会绑定到$(“#some-div”)元素上。那我们如何能解决回调函数绑定的问题?ES5中引入了一个新的方法,bind():

fun.bind(thisArg[, arg1[, arg2[, ...]]])

thisArg
当绑定函数被调用时,该参数会作为原函数运行时的this指向.当使用new 操作符调用绑定函数时,该参数无效.
arg1, arg2, ...
当绑定函数被调用时,这些参数加上绑定函数本身的参数会按照顺序作为原函数运行时的参数.

该方法创建一个新函数,称为绑定函数,绑定函数会以创建它时传入bind方法的第一个参数作为this,传入bind方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数.

$("#ele").click(person.hello.bind(person));
//相应元素被点击时,输出person says hello world

该方法也很容易模拟,看下Prototype.js中bind方法的源码:

Function.prototype.bind = function(){
  var fn = this, args = Array.prototype.slice.call(arguments), object = args.shift();
  return function(){
    return fn.apply(object,
      args.concat(Array.prototype.slice.call(arguments)));
  };
};
参考资料

js老生常谈之this,constructor ,prototype

详解JavaScript中的this

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/91625.html

相关文章

  • Javascript重温OOP之原型原型链

    摘要:在构造函数中的中定义的属性和方法,会被创建的对象所继承下来。从上面的输出结果看出,指向了其构造函数的,而本身也是一个对象,其内部也有属性,其指向的是直到最后指向,这条原型链才结束。和都指向,说明原型链到终止。 prototype原型对象 每个函数都有一个默认的prototype属性,其实际上还是一个对象,如果被用在继承中,姑且叫做原型对象。 在构造函数中的prototype中定义的属性...

    lindroid 评论0 收藏0
  • Javascript重温OOP之JS的解析执行过程

    摘要:了解面向对象编程之前,首先要了解的执行顺序。的解析过程分为两个阶段预处理阶段与执行期。在执行阶段的执行上下文对象由赋值为指向对应函数 了解js面向对象编程之前,首先要了解js的执行顺序。js的解析过程分为两个阶段:预处理阶段与执行期。 预处理阶段 在预处理阶段,js会首先创建一个执行上下文对象(Execute Context,然后扫描声明式函数和用var定义的变量,将其加入执行上下文环...

    xumenger 评论0 收藏0
  • Javascript重温OOP之作用域闭包

    摘要:的变量作用域是基于其特有的作用域链的。需要注意的是,用创建的函数,其作用域指向全局作用域。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。 作用域 定义 在编程语言中,作用域控制着变量与参数的可见性及生命周期,它能减少名称冲突,而且提供了自动内存管理 --javascript 语言精粹 我理解的是,一个变量、函数或者成员可以在代码中访问到的范围。 js的变量作...

    JessYanCoding 评论0 收藏0
  • 重温基础】15.JS对象介绍

    摘要:构造函数通常首字母大写,用于区分普通函数。这种关系常被称为原型链,它解释了为何一个对象会拥有定义在其他对象中的属性和方法。中所有的对象,都有一个属性,指向实例对象的构造函数原型由于是个非标准属性,因此只有和两个浏览器支持,标准方法是。 从这篇文章开始,复习 MDN 中级教程 的内容了,在初级教程中,我和大家分享了一些比较简单基础的知识点,并放在我的 【Cute-JavaScript】系...

    booster 评论0 收藏0
  • Javascript重温OOP之面向对象

    摘要:类的继承建立继承关系修改的指向调用父类方法调用父类的构造器调用父类上的方法封装命名空间是没有命名空间的,因此可以用对象模拟。参考资料面向对象 面向对象程序设计(Object-oriented programming,OOP)是一种程序设计范型,同时也是一种程序开发的方法。对象指的是类的实例。它将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性。——维基百...

    AbnerMing 评论0 收藏0

发表评论

0条评论

RancherLabs

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<