面向对象的Javascript如何用于DOM操作

时间:2017-01-27 12:56:57

标签: javascript oop dom

我理解OOP的主要原理,我有点知道如何将它实现为JS。

function Person(name) {
    this.name = name;
    this.speak = function(msg) {
        console.log('Person says:' + msg);
    }
}

var dad = new Person('David');
dad.speak('I am your dad!');

上面的脚本只是在控制台中打印出一条消息。我不明白我们如何使用这种技术来处理DOM。也许这样的事情?:

function Person(target, name) {
    this.target = target;
    this.name = name;
    this.speak = function(msg) {
        this.target.find('.speech-bubble').html(msg);
    }
}

var dad = new Person($('#dad'), 'David');
dad.speak('I am your dad!');

虽然这似乎不是一个好方法。

我们如何通过OO Javascript使用对象,方法,构造函数等来操作DOM?

2 个答案:

答案 0 :(得分:3)

您需要了解的是Prototype

的概念

使用new创建实例时,您将基于原型构建对象。

请考虑以下事项:

function Person(name) {
    this.name = name;
    this.speak = function (msg) {
        console.log('Person says:' + msg);
    };
}
var dad = new Person('David');
dad.speak('I am your dad!');
console.log('Is dad.speak equal to dad.speak?', dad.speak === dad.speak);
var mom = new Person('David');
console.log('Is mom.speak equal to dad.speak?', mom.speak === dad.speak);

每次构造Person的新实例时,新的speak原型现在会浮动在逻辑中的某个位置。这是非常低效的。

要解决此问题,我们需要修改函数的prototype

function Person(name) {
    this.name = name;
}
Person.prototype.speak = function (msg) {
    console.log('Person says:' + msg);
};
var dad = new Person('David');
dad.speak('I am your dad!');
console.log('Is dad.speak equal to dad.speak?', dad.speak === dad.speak);
var mom = new Person('David');
console.log('Is mom.speak equal to dad.speak?', dad.speak === dad.speak);

这样,我们只在prototype上创建了一次继承给所有实例的函数。这更容易维护,效率更高。

现在我们可以通过他们的prototype扩展DOM对象,但不建议这样做,因为你开始搞乱网络标准,使故障排除更加困难。

Array.prototype.isLengthGreaterThanFive = function(thisArg) {
  return this.length > 5;
};
console.log([1, 2, 3, 4].isLengthGreaterThanFive(), [1, 2, 3, 4, 5, 6].isLengthGreaterThanFive());

更好的处理方法是创建扩展对象或简单地使用函数:

//Using functions
function isLengthGreaterThanFive(array) {
  return array.length > 5;
}
console.log(isLengthGreaterThanFive([1, 2, 3, 4]), isLengthGreaterThanFive([1, 2, 3, 4, 5, 6]));

//Using a wrapper class
var MyArray = (function() {
  function MyArray(array) {
    if (array === void 0) {
      array = [];
    }
    this.array = array;
  }
  MyArray.prototype.isLengthGreaterThanFive = function() {
    return this.array.length > 5;
  };
  return MyArray;
}());
console.log(new MyArray([1, 2, 3, 4]).isLengthGreaterThanFive(), new MyArray([1, 2, 3, 4, 5, 6]).isLengthGreaterThanFive());

使用类的好处是我们可以扩展我们对该对象的想法:

//Base class
function Person(firstname, lastname, says) {
    if (firstname === void 0) {
        firstname = "Leonado";
    }
    this.firstname = firstname;
    if (lastname === void 0) {
        lastname = "Da Vinci";
    }
    this.lastname = lastname;
    if (says === void 0) {
        says = "hello";
    }
    this.says = says;
}
//Base methods
Person.prototype.iAm = function () {
    return this.firstname + " " + this.lastname;
};
Person.prototype.Speak = function () {
    return this.says + " my name is " + this.iAm();
};
//Extended class
function Warrior(firstname, lastname, says) {
    //Call in constructor
    Person.call(this, firstname, lastname, says);
}
//Inheriting
Warrior.prototype = Object.create(Person.prototype);
Warrior.prototype.constructor = Warrior;
//Overruling "Speak"
Warrior.prototype.Speak = function () {
    return "My name is " + this.iAm() + ", " + this.says;
};
console.log([new Warrior("Peter", "Allan", "Ahoyhoy").Speak(), new Person("Peter", "Allan", "Ahoyhoy").Speak()]);

在上面的示例中,我们为Person扩展了Warrior的原型,以便我们保留Person的功能,然后简单地修改Warrior的不同之处。通过这种方式,我们可以重用原型方法iAm,我们可以专注于只更改Speak方法中需要更改的内容。

编辑1

我注意到这个问题已经改变了一点。

您可以像使用JavaScript中的任何其他类一样处理DOM元素。以下设置让所有Persons共享一个DIVspeakUp

var Person = (function () {
    function Person(age, firstname, lastname) {
        if (age === void 0) { age = 50; }
        if (firstname === void 0) { firstname = "Peter"; }
        if (lastname === void 0) { lastname = "Venkman"; }
        this.age = age;
        this.firstname = firstname;
        this.lastname = lastname;
    }
    Person.prototype.speakUp = function () {
        Person.bubble.innerHTML = this.firstname + " " + this.lastname + " is " + this.age + " years old";
    };
    return Person;
}());
Person.bubble = document.createElement("div");
document.body.appendChild(Person.bubble);
setInterval(function () {
    var p = new Person(Math.floor(Math.random() * 100));
    p.speakUp();
}, 3000);

这很容易变成DIVPerson个,或者在所有Person之间共享的引用DOM对象(document.getElementById)。

编辑2

回应你的评论:

在JavaScript中,一切都在本质上,object。您创建了一个函数,它使用函数名称注册object并返回instance的{​​{1}}。 objectArraysStrings元素和自定义函数等所有内容都隐藏在幕后的DOM。每次创建新的objectArray元素或其他任何元素时,它都会引用其主对象(称为原型)。这被称为原型链。

如果查看上面的第二个示例,当调用DOM时,JavaScript首先在实例中搜索dad.speak属性,但是找不到它,因为我们没有按照方式分配它我们在第一个例子中做的是它是特定于实例的。

然后JavaScript会在speak链上尝试一个级别,在这里它将找到匹配的属性并使用它。这样我们就可以改变JavaScript中自定义 OR 现有元素的默认行为。

这个想法是,如果你有一些属性原型的所有实例应该有,那么我们只需修改一次原型,它们将prototype这个属性。

这样想。如果你用JavaScript描述地球上的所有生物,你会想要某种形式的分组。例如,第一级将类似于inherit对象,其中包含名称和id的属性。在这里,您可以创建ExistsPlant并让它们都继承Animal的原型。现在我们可以创建一个继承Exists的{​​{1}}类和一个继承Flower的{​​{1}}类,等等。

这个想法是以一种通过继承对人类有意义的方式来应用你的属性(一只猫头鹰可以飞,因为它是一只鸟/鲨鱼可以游泳,因为它是一条鱼)。将它们绑定在有意义的层面,以逻辑模式继承并有效地利用你的时间。

如果您仍然感到困惑,请尝试查找Plant教程。

这是一个很好的Youtube视频来解释它:

https://www.youtube.com/watch?v=PMfcsYzj-9M

答案 1 :(得分:3)

关于面向对象,如果你要采用面向代码的DOM,你就不会太过分了。

我会说一个类应该代表DOM上的一个组件/元素。它的方法是国家管理部分。但说实话,这里没有正确的答案。这只是设计面向对象的DOM的OO的一种方式。

示例:

const basicClassName = 'component';
const basicTemplate = '<h1>This is my basic component</h1>';

class MyComponent {
  constructor(template = basicTemplate, className = basicClassName) {
    this.template = template;
    this.className = className;
    
    this.element = document.createElement('div');
    this.element.className = className;
    this.element.innerHTML = template;
    this.element.onclick = this.onClick.bind(this);
    this.element.style.cursor = 'pointer';
  }
  
  onClick() {
    this.element.classList.toggle('clicked');
  }
}

const component = new MyComponent();

const container = document.querySelector('.container');

container.appendChild(component.element);
body {
  font-size: 14px;
}

.component {
  display: block;
  padding: 1.3em;
  box-shadow: 1px 1px 4px lightgray;
}


.clicked {
  background-color: papayawhip;
}
<div class="container"></div>