Herramientas de usuario

Herramientas del sitio


wiki2:js

JavaScript

Objects

Create

There are three ways to create an object:

var newObject = {};
var newObject = Object.create( Object.prototype );
var newObject = new Object();

Properties

// 1. Dot syntax
newObject.someKey = "Hello World";
var value = newObject.someKey;
 
// 2. Square bracket syntax
// Set properties
newObject["someKey"] = "Hello World";
 
 
// 3. Object.defineProperty
Object.defineProperty( newObject, "someKey", {
    value: "for more control of the property's behavior",
    writable: true,
    enumerable: true,
    configurable: true
});
 
// If the above feels a little difficult to read, a short-hand could
// be written as follows:
var defineProp = function ( obj, key, value ){
  var config = {
    value: value,
    writable: true,
    enumerable: true,
    configurable: true
  };
  Object.defineProperty( obj, key, config );
};
 
// To use, we then create a new empty "person" object
var person = Object.create( Object.prototype );
defineProp( person, "car",  "Delorean" );
defineProp( person, "dateOfBirth", "1981" );
defineProp( person, "hasBeard", false );
 
// 4. Object.defineProperties
Object.defineProperties( newObject, {
  "someKey": {
    value: "Hello World",
    writable: true
  },
  "anotherKey": {
    value: "Foo bar",
    writable: false
  }
});

Inheritance

var driver = Object.create( person );
defineProp(driver, "topSpeed", "100mph");

Constructors

function Car( model, year, miles ) {
  this.model = model;
  this.year = year;
  this.miles = miles;
  this.toString = function () {
    return this.model + " has done " + this.miles + " miles";
  };
}
 
var civic = new Car( "Honda Civic", 2009, 20000 );
var mondeo = new Car( "Ford Mondeo", 2010, 5000 );

Closures, callbacks and promises

https://gist.github.com/amysimmons/3d228a9a57e30ec13ab1

For a code newbie like myself, callbacks, closures and promises are scary JavaScript concepts.

10 months into my full-time dev career, and I would struggle to explain these words to a peer.

So I decided it was time to face my fears, and try to get my head around each concept.

Here are the notes from my initial reading. I'll continue to refine them as my understanding improves.

Closures

In JavaScript, if you use the function keyword inside another function, you are creating a closure

A regular function created in the global scope can also close over variables

The below code has a closure because the anonymous function function() { console.log(text); } is declared inside another function, sayHello2()

function sayHello2(name) {
    var text = 'Hello ' + name; // Local variable
    var say = function() { console.log(text); }
    return say;
}
var say2 = sayHello2('Bob');
say2(); // logs "Hello Bob"

If you declare a function within another function, then the local variables can remain accessible after returning from the function you called

This is demonstrated above, because we call the function say2() after we have returned from sayHello2(). The code that we call is still able to reference the variable 'text', which was a local variable of the function sayHello2()

A closure is an inner function that has access to the outer (enclosing) function's variables The closure has three scopes, all part of the same chain: it has access to its own scope (variables defined between its curly brackets), it has access to the outer function's variables, and it has access to the global variables

The inner function has access not only to the outer function’s variables, but also to the outer function's parameters

function showName (firstName, lastName) {
	var nameIntro = "Your name is ";
	
    	// this inner function has access to the outer function's variables, including the parameter​
	function makeFullName () {  
		return nameIntro + firstName + " " + lastName;
	}
	
	return makeFullName ();
}

showName ("Amy", "Simmons"); // Your name is Amy Simmons
//this example uses a closure because:
//a function is defined inside another function
//the inner function has access to the outer functions variables, 'text'
//the outer function's variables can be accessed even after the outer function is called

function surpriseMe(surprise){
	var text = "Today you will be surprised with " + surprise;
  
  function revealSurprise(){
  	alert(text);
  }
  
  return revealSurprise;
}

var surprise = surpriseMe("1000 puppy dogs");
surprise();

Callbacks

Take the following example: a user interaction triggers a request to a server, and the response from the server should be displayed in the browser

A synchronous way of doing this would be:

//synchronous example:

request = prepare_the_request();
response = send_request_synchronously(request);
display(response)

Because the above code is synchronous, the program will wait for it to finish before moving on to another task

If either the network or the server is slow, the user will be left waiting

A better way of doing this would be with an asynchronous request, which provides a 'callback' function which will be invoked once the server's response is received:

//asynchronous example:

request = prepare_the_request();
response = send_request_asynchronously(request, function (response) {
	display(response);
});

When you execute something asynchronously, the program can move on to another task before the request finishes

A callback function is a function which is:

  • passed as an argument to another function
  • is invoked after some kind of event
  • once its parent function completes, the function passed as an argument is then called

A callback is any function that is called by another function, which takes the first function as a parameter

Consider how programmers normally write to a file:

fileObject = open(file)
//now that we have WAITED for the file to open, we can write to it

fileObject.write("We are writing to the file.")
//now we can continue doing the other, totally unrelated things our program does

In the above example, we wait for the file to open, before we write to it.

This blocks the flow of execution, and our program cannot do any of the other things it might need to do

This is where callbacks are useful:

//we pass writeToFile (a callback function) to the open function
fileObject = open(file, writeToFile)

//execution continues flowing -- we don't wait for the file to be opened
//once the file is opened we write to it, but while we wait we can do other things
//the callback function
function done(){
	console.log("Done");
}

//the parent function 
function increment(num, callBack){
	for(var i = 0; i <= num; i++){
  	console.log(i);
  }
  return callBack();
}

//the callback function is passed to the increment function 
increment(10, done);

Promises

Normally code is synchronous - one statement executes and there is a guarantee that the next statement will execute immediately afterwards

With asynchronous operations, you should assume that you have no idea when the operation will complete. You can't even assume that just because you send out one request first, and another request second, that they will return in that order

Callbacks are the standard way of handling asynchrnous code in JavaScript, but promises are the best way to handle asynchronous code. This is because callbacks make error handling difficult, and lead to ugly nested code.

Promises help you naturally handle errors, and write cleaner code by not having callback parameters

A promise represents the result of an asynchronous operation. A promise is in one of three different states: pending, fulfilled or rejected

Once a promise is fulfilled or rejected, it is immutable (i.e. it can never change again)

We use new Promise to construct the promise, the constructor is called immediately with two arguments - one that fulfils the promise and the other that rejects the promise promise.done allows us to wait for the promise to be fulfilled or rejected before doing something with it

Basic usage:

var p = new Promise(function(resolve, reject) {
	
	// Do an async task async task and then...

	if(/* good condition */) {
		resolve('Success!');
	}
	else {
		reject('Failure!');
	}
});

p.then(function() { 
	/* do something with the result */
}).catch(function() {
	/* error :( */
})

A realistic example of using promises would be converting a get request to a promise-based task:

// From Jake Archibald's Promises and Back:
// http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promisifying-xmlhttprequest

function get(url) {
  // Return a new promise.
  return new Promise(function(resolve, reject) {
    // Do the usual XHR stuff
    var req = new XMLHttpRequest();
    req.open('GET', url);

    req.onload = function() {
      // This is called even on 404 etc
      // so check the status
      if (req.status == 200) {
        // Resolve the promise with the response text
        resolve(req.response);
      }
      else {
        // Otherwise reject with the status text
        // which will hopefully be a meaningful error
        reject(Error(req.statusText));
      }
    };

    // Handle network errors
    req.onerror = function() {
      reject(Error("Network Error"));
    };

    // Make the request
    req.send();
  });
}

// Use it!
get('story.json').then(function(response) {
  console.log("Success!", response);
}, function(error) {
  console.error("Failed!", error);
});

Promises wrap an asynchronous action in an object, which can be passed around and told to do certain things when the action finishes or fails

Simialr to the above example:

funciton get(url){
	return new Promise(function(succeed, fail){
		var req = new XMLHttpRequest();
		req.open("GET", url, true);
		req.addEventListener("load", function(){
			if(req.status < 400){
				succeed(req.responseText);
			}else{
				fail(new Error("Request failed: " + req.statusText));
			}
		});
		req.addEventListener("error", function(){
			fail(new Error("Network error"));
		});
		req.send(null);
	});
}

The get function above receives a url, and returns a promise

The promise has a .then method which can be called with two functions, one to handle success, and the other to handle failure

get("example/data.txt").then(function(text){
	console.log("data.txt: " + text);
}, function(error){
	console.log('Failed to fetch data.txt: " + error);
});

Calling .then produces a new promise, whose result depends on the return value of the first function we passed to then

You can also wait for all promises:

var p1 = Promise.resolve(3);
var p2 = 1337;
var p3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');
}); 

Promise.all([p1, p2, p3]).then(values => { 
  console.log(values); // [3, 1337, "foo"] 
});

How reject is managed:

var p1 = new Promise((resolve, reject) => { 
  setTimeout(resolve, 1000, 'one'); 
}); 
var p2 = new Promise((resolve, reject) => { 
  setTimeout(resolve, 2000, 'two'); 
});
var p3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 3000, 'three');
});
var p4 = new Promise((resolve, reject) => {
  setTimeout(resolve, 4000, 'four');
});
var p5 = new Promise((resolve, reject) => {
  reject('reject');
});

Promise.all([p1, p2, p3, p4, p5]).then(values => { 
  console.log(values);
}).catch(reason => { 
  console.log(reason)
});

//From console: 
//"reject"

Modules

Immediately-Invoked Function Expression (IIFE) is a anonymous function which invokes itself.

//Module Starts

(function(window){

    var sum = function(x, y){
        return x + y;
    }

    var sub = function(x, y){
        return x - y;
    }

    var math = {
        findSum: function(a, b){
            return sum(a,b);
        },

        findSub: function(a, b){
            return sub(a, b);
        }
    }

    window.math = math;

})(window);

//Module Ends


console.log(math.findSum(1, 2)); //3
console.log(math.findSub(1, 2)); //-1

Here sum and sub function objects remain in memory but there is no way the program which uses the module can access them therefore we prevented any chances of overriding global variables. The only thing thats available global is math object which exposes all the functionality of the module by hiding their implementation.

CommonJS

CommonJS is a non-browser JavaScript specification for creating modules.

Suppose we have two files “main.js” and “math.js”. Here is the code in “math.js” file

var sum = function(x, y){
    return x + y;
}

var sub = function(x, y){
    return x - y;
}

var math = {
    findSum: function(a, b){
        return sum(a,b);
    },

    findSub: function(a, b){
        return sub(a, b);
    }
}

//All the variable we want to expose outside needs to be property of "exports" object.
exports.math = math;

Here is the code in “main.js” file

//no file extension required
var math = require("./math").math;
console.log(math.findSum(1, 2)); //3
console.log(math.findSub(1, 2)); //-1

Asynchronous Module Definition (AMD)

AMD is a JavaScript browser specification for creating modules. One such AMD library is RequireJS.

Universal Module Definition

UMD is a set of techniques to create modules that can be imported using CommonJS, AMD or as IIFE. UMD modules are called Universal Modules. There are many techniques under UMD. Its upto you to choose whichever you want to use for creating modules. My favourite UMD technique is returnExports.

ECMAScript 6 (ES6)

“let” lets you define block scope(bracket scope) variables in JavaScript.

if(true)
{
    let x = 12;
    alert(x); //alert's 12
}

alert(x); //x is undefined here

“const” lets you define read only variables using JavaScript. Variables created using “const” are block scoped(or bracket scoped). Redeclaring a “const” variable in the same scope throws an error.

const x = 12;

//an constant 'x' is already available in this scope therefore the below line throws an error when you are try to create a new x variable.
const x = 13;

if(true)
{
    //an constant 'x' is available in this scope but not defined in this scope therefore the below line will not throw error instead define a new "x" inside this scope.
    const x = 13;
    
    //here 'y' is available inside this scope not outside this scope
    const y = 11;
}

//here creating a new 'y' will not throw an error because no other 'y' is available in this scope(i.e., global scope)
const y = 12;

Return multiple values

function function_name()
{
    return [1, 6, 7, 4, 8, 0]; //here we are storing variables in an array and returning the array
}

var q, w, e, r, t, y;

//Here we are using ES6's array destructuring feature to assign the returned values to variables.
//Here we are ignoring 2 and 4 array indexes
[q, w, , r, , y] = function_name();

alert(y);//y is 0

Default argument values

function myFunction(x = 1, y = 2, z = 3)
{
     console.log(x, y, z); // Output "6 7 3"
}
myFunction(6,7);

Also, passing undefined is considered as missing an argument. Here is an example to demonstrate this:

   function myFunction(x = 1, y = 2, z = 3)
   {
     console.log(x, y, z); // Output "1 7 9"
   }
   myFunction(undefined,7,9);

Template Strings

var a = 5;
var b = 10;
console.log(`Fifteen is ${a + b} and
not ${2 * a + b}.`);
// "Fifteen is 15 and
// not 20."
var str = String.raw`Hi\n${2+3}!`;
// "Hi\n5!"
// Simple string substitution
var name = "Brendan";
console.log(`Yo, ${name}!`);
// => "Yo, Brendan!"
var greeting = "Yo \
World";

console.log(`string text line 1
string text line 2`);

ES6 Modules

export class Math {

    constructor()
    {
        this.sum = function(x, y){
            return x + y;
        }

        this.sub = function(x, y){
            return x - y;
        }
    }

    findSum(a, b)
    {
        return this.sum(a, b);
    }

    findSub(a, b)
    {
        return this.sub(a, b);
    }
}
import {Math} from 'math';

var math = new Math();

console.log(math.findSum(1, 2)); //3
console.log(math.findSub(1, 2)); //-1

The ...args operator (spread operator)

//args variable is an array holding the passed function arguments
function function_one(...args)
{   
    console.log(args);
    console.log(args.length);
}

function_one(1, 4);
function_one(1, 4, 7);
function_one(1, 4, 7, 0);


function function_two(a, b, ...args)
{
    console.log(args);
    console.log(args.length);
}

//"args" holds only 7 and 9
function_two(1, 5, 7, 9);
//args variable is an array holding the function arguments
function function_one()
{   
    var args = Array.prototype.slice.call(arguments, function_one.length);

    console.log(args);
    console.log(args.length);
}

function_one(1, 4);
function_one(1, 4, 7);
function_one(1, 4, 7, 0);


function function_two(a, b)
{
    var args = Array.prototype.slice.call(arguments, function_two.length);

    console.log(args);
    console.log(args.length);
}

//"args" holds only 7 and 9
function_two(1, 5, 7, 9);
function function_name(a, b)
{
    console.log(a+b);
}

var array = [1, 4];

function_name(...array); //is equal to function_name(1, 4)

for loop

var array = [1, 3, 5, 7, 9];

//'i' references to the values of the array indexes
for(var i of array)
{
    console.log(i); //1, 3, 5, 7, 9
}

Iterators

We need to implement Symbol.iterator property on an custom collection. Symbol.iterator returns a Iterator object i.e., a object with next() property. next() is invoked by for of until next() returns {value: undefined, done: true}. To continue looping and return an collection element next() has to return {value: element_value, done: false}.

var custom_collection = {
    elements:  [1, 4, 6, 9],
    size : 3,
    pointer :0,
    [Symbol.iterator]:  function(){
        var e = this.elements;
        var s = this.size;
        var p = this.pointer;
        return{
            next: function() {
                if(p > s) 
                {
                    return { value: undefined, done: true };
                } 
                else 
                {
                    p++;
                    return { value: e[p - 1], done: false };
                }
            },
        };
    }
}

for(var i of custom_collection)
{
    console.log(i); //1, 4, 6, 9
}

Generators

JavaScript’s yield keyword and function*() syntax together make JS Generators.

In nutshell JavaScript generators provide a new way for functions to return a collection and also a new way of looping(or iterating) through the elements of the returned collection.

function* collection_name()
{
    yield 1;
    yield 3;
    yield 5;
    yield 7;
}

for(var iii  of collection_name())
{
    console.log(iii);
}

Classes

class Student
{
    //constructor of the class
    constructor(name, age)
    {
        //"this" points to the current object
        this.name = name;

        this._age = age;
    }

    //member function
    getName()
    {
        return this.name;
    }

    setName(name)
    {
        this.name = name;
    }

    //getters and setters make a function accessible like a variable. They are used as wrappers around other variables.
    set age(value)
    {
        this._age = value;
    }

    get age()
    {
        return this._age;
    }
}

//class person inherits student class
class Person extends Student
{
    constructor(name, age, citizen)
    {
        //this points to the person class
        this.citizen = citizen;

        //call constructor of super class. "super" is an pointer to the super class object
        super(name, age);
    }

    getCitizen()
    {
        return this.citizen;
    }

    //overriding
    getName()
    {
        //we are calling the super class getName function
        return super.getName();
    }
}

//instance of student class
var stud = new Student("Narayan", 21);

//instance of person class
var p = new Person("Narayan Prusty", 21, "India");

stud.age = 12; //executes setter
console.log(stud.age); //executes getter

Lambdas

//sum is the function name
//x and y are function parameters
var sum = (x, y) => x + y;
console.log(sum(2, 900)); //902
var sum = (x, y) => {
    x = x + 10;
    y = y + 10;
    return x + y;
}
console.log(sum(10, 10)); //40
function sum(p, q)
{
    console.log(p() + q()); //87
}

sum(a => 20 + 10, b => 1 + 56); //here we are passing two function objects

The “this” pointer inside an asynchronously executed arrow function points to the scope inside which it was passed as callback. A regular function’s this pointer points to global scope when executed asynchronously.

 
window.age = 12;

function Person(){
  this.age = 34;

  setTimeout(() => {
    console.log(this.age); //34, which is from the current scope
  }, 1000);

  setTimeout(function(){
    console.log(this.age); //12, which is global (as setTimeout)
  }, 1000);  
}

var p = new Person();

Sets

> var set = new Set();
undefined
> set
Set {}
> set.add(1)
Set { 1 }
> set.add(2)
Set { 1, 2 }
> set.add(0)
Set { 1, 2, 0 }
> set.add(1)
Set { 1, 2, 0 }
> 
//create a set
var set = new Set();

//add three keys to the set
set.add({x: 12});
set.add(44);
set.add("text");

//check if a provided key is present
console.log(set.has("text"));

//delete a key
set.delete(44);

//loop through the keys in an set
for(var i of set)
{
    console.log(i);
}

//create a set from array values
var set_1 = new Set([1, 2, 3, 4, 5]); 

//size of set
console.log(set_1.size); //5

//create a clone of another set
var set_2 = new Set(set.values());

Maps

//create a map
var map = new Map();

//add three keys & values to the map
map.set({x: 12}, 12);

//same key is overwritten
map.set(44, 13);
map.get(44);
map.set(44, 12);

//check if a provided key is present
console.log(map.has(44)); //true

//retrieve key
console.log(map.get(44)); //12

//delete a key
map.delete(44);

//loop through the keys in an map
for(var i of map)
{
    console.log(i);
}

//delete all keys
map.clear();

//create a map from arrays
var map_1 = new Map([[1, 2], [4, 5]]); 

//size of map
console.log(map_1.size); //2

map_1.get(4);

Javascript Snippets

Merge two objects

Object.assign(obj1, obj2);
let newObject = Object.assign({}, obj1, obj2, obj3, etc);

URL parameters

var urlParams = new URLSearchParams(window.location.search);

console.log(urlParams.has('post')); // true
console.log(urlParams.get('action')); // "edit"
console.log(urlParams.getAll('action')); // ["edit"]
console.log(urlParams.toString()); // "?post=1234&action=edit"
console.log(urlParams.append('active', '1')); // "?post=1234&action=edit&active=1"

Local Storage

window.localStorage.setItem('test', 'hola');
  • setItem(): Add key and value to localStorage
  • getItem(): Retrieve a value by the key from localStorage
  • removeItem(): Remove an item by key from localStorage
  • clear(): Clear all localStorage
  • key(): Passed a number to retrieve nth key of a localStorage

JavaScript Notes

> b = a.c || 4;
4

ES6 Events

const EventEmitter = require('events');

class Client extends EventEmitter
{
    eventTest() {
        this.emit('event');
    }
}

let testClient = new Client(1,2,3,4,5);
testClient.once('event', () => {console.log('triggerd1!')} );
testClient.on('event', () => {console.log('triggerd2!')} );
testClient.eventTest();
testClient.eventTest();

ES6 Events to Promises

pues es tema de que se encolan los errores. Es decir, pongamos que tengo varias promises, una para (yo que sé) login, logout, cookies. Todas con su then y catch. De repente hay un error en logout, se lanzarán todos los errores (login, logout y cookies). Aún peor, si pongo un if en logout para recibir “error de logout” el evento se recibirá en todos, pero ya no se volverá a recibir en logout. La coña de las promises es que una vez se lancen no lo vuelven a hacer, he de hacer un control muy bestia de errores (no me vale pillar el evento con once).

const EventEmitter = require('events');

class Client extends EventEmitter
{
    constructor() {
        super();
        this.num = 0;
    }
}

class Wrapper
{
    constructor (tclient) {
        this.client = tclient;
    }

    login() {
        return new Promise(function (resolve, reject) {
            this.client.on('login', () => {
                console.log("received event login");
                resolve();
            });
            this.client.on('eerror', (error) => {
                if (error === "login error") reject(error);
            });
        }.bind(this));
    }

    logout() {
        return new Promise(function (resolve, reject) {
            this.client.on('logout', () => {
                console.log("received event logout");
                resolve();
            });
            this.client.on('eerror', (error) => {
                if (error === "logout error") reject(error);
            });
        }.bind(this));
    }
}

let client = new Client();
let wrapper = new Wrapper(client);

var p1 = wrapper.login().then(function() { 
	console.log("login");
}).catch(function(error) {
	console.error("Reception error login");
});

var p2 = wrapper.logout().then(function() { 
	console.log("logout");
}).catch(function(error) {
	console.error("Reception error logout");
});


setTimeout(function() {
    client.emit('eerror', 'login error');
    client.emit('logout');
    client.emit('logout');
    client.emit('logout');
    client.emit('eerror', 'logout error');
}, 1000 * 1);

JQuery (again)

<a href="#" class="deleteUser" data-id="33" />

// Now we can take it with JQuery like this: 
$('.deleteUser').on('click', function() {
  $(this).data('id')
});

JavaScript Development Tools

Webpack

webpack src/js/app.js dist/bundle.js es el comando básico, una vez añadido webpack, para generar un bundle.js. Webpack puede realizar otras tareas de optimización y minimización si añadimos que es para producción con -p.

Lo que hace es pillar a partir de los imports dentro del código js qué construir. Módulos y plugins que utiliza Webpack para CSS también requieren que incluyas ficheros .css en el código js.

Añadiendo webpack-dev-server como pacate al proyecto y substituyendo el comando de ejecución webpack por webpack-dev-server se nos añade un dev web server que nos permite hacer reload de los ficheros y demás. El comando sería: webpack-dev-server –entry ./src/js/apps.js —output ./dist/bundle.js.

Configuración

Webpack necesita un entry point y un output. Usa modulos y plugins para transformar el contenido desde el entry al output.

Para escoger un fichero config (diferente al por defecto: webpack.config.js) se puede añadir –config al comando. Con un archivo por defecto no necesitamos añadir parámetros al comando de ejecución.

var path = require('path');

module.exports = {
  entry: './src/js/app.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
    publicPath: '/dist'  // Para el webpack-dev-server, donde encontrar los ficheros
  }
}
Modulos

Tras el output podemos añadir una sección de modules. Modules tiene un array de rules que es sobre qué elementos se aplica un módulo. Un módulo es un transformador del código y lo has de instalar previamente (npm install css-loader style-loader –save-dev).

Las reglas (rules) son un array de objetos js con varias propiedades. La primera es una expresión regular correspondiente a los nombres de ficheros, test, si se valida se ejecutará una serie de “comandos” dentro del array de use.

More JavaScript

Default arguments on functions

const defaultPerson = {
name: {
first: "Shane",
last: "McConkey"
},
favActivity: "skiing"};
function logActivity(person = defaultPerson) {
console.log(`${person.name.first} loves ${person.favActivity}`);
}

Arrow functions

This:

const lordify = function(firstName) {
return `${firstName} of Canterbury`;
};

Is the same than this:

const lordify = firstName => `${firstName} of Canterbury`;

Arrow functions protect the scope of this.

For example this fails:

const tahoe = {
  mountains: ["Freel", "Rose", "Tallac", "Rubicon", "Silver"],
  print: function(delay = 1000) {
    console.log(this); // Window {}
    setTimeout(function() {
      console.log(this.mountains.join(", "));
    }, delay);
}};
tahoe.print(); // Uncaught TypeError: Cannot read property 'join' of undefined

But this not:

const tahoe = {
	mountains: ["Freel", "Rose", "Tallac", "Rubicon", "Silver"],
	print: function(delay = 1000) {
		setTimeout(() => {
			console.log(this.mountains.join(", "));
		}, delay);
	}
};
tahoe.print(); // Freel, Rose, Tallac, Rubicon, Silver
wiki2/js.txt · Última modificación: 2022/11/03 16:21 (editor externo)