Skip to main content

7 posts tagged with "JavaScript"

View All Tags

Marble testing for rxjs

· 2 min read

Open in Notion

Examples

'-' or '------': Equivalent to [NEVER](<https://rxjs.dev/api/index/const/NEVER>), or an observable that never emits or errors or completes.

|: Equivalent to [EMPTY](<https://rxjs.dev/api/index/const/EMPTY>), or an observable that never emits and completes immediately.

#: Equivalent to [throwError](<https://rxjs.dev/api/index/function/throwError>), or an observable that never emits and errors immediately.

'--a--': An observable that waits 2 "frames", emits value a on frame 2 and then never completes.

'--a--b--|': On frame 2 emit a, on frame 5 emit b, and on frame 8, complete.

'--a--b--#': On frame 2 emit a, on frame 5 emit b, and on frame 8, error.

'-a-^-b--|': In a hot observable, on frame -2 emit a, then on frame 2 emit b, and on frame 5, complete.

'--(abc)-|': on frame 2 emit ab, and c, then on frame 8, complete.

'-----(a|)': on frame 5 emit a and complete.

'a 9ms b 9s c|': on frame 0 emit a, on frame 10 emit b, on frame 9,011 emit c, then on frame 9,012 complete.

'--a 2.5m b': on frame 2 emit a, on frame 150,003 emit b and never complete.

import { TestScheduler } from 'rxjs/testing';
import { throttleTime } from 'rxjs';

const testScheduler = new TestScheduler((actual, expected) => {
// asserting the two objects are equal - required
// for TestScheduler assertions to work via your test framework
// e.g. using chai.
expect(actual).deep.equal(expected);
});

// This test runs synchronously.
it('generates the stream correctly', () => {
testScheduler.run((helpers) => {
const { cold, time, expectObservable, expectSubscriptions } = helpers;
const e1 = cold(' -a--b--c---|');
const e1subs = ' ^----------!';
const t = time(' ---| '); // t = 3
const expected = '-a-----c---|';

expectObservable(e1.pipe(throttleTime(t))).toBe(expected);
expectSubscriptions(e1.subscriptions).toBe(e1subs);
});

testScheduler.run((helpers) => {
const { time, cold } = helpers;
const source = cold('---a--b--|');
const t = time(' --| ');
// --|
const expected = ' -----a--b|';
const result = source.pipe(delay(t));
expectObservable(result).toBe(expected);
});

testScheduler.run((helpers) => {
const { animate, cold } = helpers;
animate(' ---x---x---x---x');
const requests = cold('-r-------r------');
/* ... */
const expected = ' ---a-------b----';
});
});

Prototype in JavaScript

· One min read

Open in Notion

All JavaScript objects inherit properties and methods from a prototype.

  • Date objects inherit from Date.prototype
  • Array objects inherit from Array.prototype
  • Person objects inherit from Person.prototype

The Object.prototype is on the top of the prototype inheritance chain:

Date objects, Array objects, and Person objects inherit from Object.prototype

Example

function Person(first, last, age, eyecolor) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eyecolor;
}

Person.prototype.nationality = "English";

Person.prototype.name = function() {
return this.firstName + " " + this.lastName;
};

var myFather = new Person("John", "Doe", 50, "blue");
var myMother = new Person("Sally", "Rally", 48, "green");

❗Please do not add a new property to an existing object constructor as below. That is a wrong way.

Person.nationality = "English";

Syntax Tips

· One min read

Open in Notion

?? and ||

a || b // equals: a ? a : b
a ?? b // equals: a != undefined && a != null ? a : b
!'' // output: true
0 ?? 'a' // output: 0
0 || 'a' // output: "a"
'' ?? 'a' // output: ""
'' || 'a' // output: "a"

call, apply, bind

These methods can change the this point.

function test(arg1, arg2) {};

test.call(null, a1, a2);
test.apply(null, [a1, a2]);

var t = test.bind(null);
t();

AMD、CMD、UMD、CommonJS

· 3 min read

Open in Notion

Asynchronous Module Definition (AMD) has gained traction on the frontend, with RequireJS being the most popular implementation.

Here’s module foo with a single dependency on jquery:

// filename: foo.js
define(['jquery'], function ($) {
// methods
function myFunc(){};

// exposed public methods
return myFunc;
});

And a little more complicated example with multiple dependencies and multiple exposed methods:

// filename: foo.js
define(['jquery', 'underscore'], function ($, _) {
// methods
function a(){}; // private because it's not returned (see below)
function b(){}; // public because it's returned
function c(){}; // public because it's returned

// exposed public methods
return {
b: b,
c: c
}
});

CMD(Common Module Definition)

Standard locates at https://github.com/seajs/seajs/issues/242. It keeps more compatibilities with CommonJS and Node.js Modules.

  • Published by Chinese people who is developing SeaJS.
  • It is like AMD.
define((require, exports, module) => {
module.exports = {
fun1: () => {
var $ = require('jquery');
return $('#test');
}
};
});

CommonJS

CommonJS is a style you may be familiar with if you’re written anything in Node (which uses a slight variant). It’s also been gaining traction on the frontend with Browserify.

Using the same format as before, here’s what our foo module looks like in CommonJS:

// filename: foo.js

// dependencies
var $ = require('jquery');

// methods
function myFunc(){};

// exposed public method (single)
module.exports = myFunc;

And our more complicate example, with multiple dependencies and multiple exposed methods:

// filename: foo.js
var $ = require('jquery');
var _ = require('underscore');

// methods
function a(){}; // private because it's omitted from module.exports (see below)
function b(){}; // public because it's defined in module.exports
function c(){}; // public because it's defined in module.exports

// exposed public methods
module.exports = {
b: b,
c: c
};

UMD(Universal Module Definition)

Since CommonJS and AMD styles have both been equally popular, it seems there’s yet no consensus. This has brought about the push for a “universal” pattern that supports both styles, which brings us to none other than the Universal Module Definition.

The pattern is admittedly ugly, but is both AMD and CommonJS compatible, as well as supporting the old-style “global” variable definition:

(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// Node, CommonJS-like
module.exports = factory(require('jquery'));
} else {
// Browser globals (root is window)
root.returnExports = factory(root.jQuery);
}
}(this, function ($) {
// methods
function myFunc(){};

// exposed public method
return myFunc;
}));

And keeping in the same pattern as the above examples, the more complicated case with multiple dependencies and multiple exposed methods:

(function (root, factory) {
if (typeof define === 'function' &amp;&amp; define.amd) {
// AMD
define(['jquery', 'underscore'], factory);
} else if (typeof exports === 'object') {
// Node, CommonJS-like
module.exports = factory(require('jquery'), require('underscore'));
} else {
// Browser globals (root is window)
root.returnExports = factory(root.jQuery, root._);
}
}(this, function ($, _) {
// methods
function a(){}; // private because it's not returned (see below)
function b(){}; // public because it's returned
function c(){}; // public because it's returned

// exposed public methods
return {
b: b,
c: c
}
}));

Download File via AJAX

· One min read

Open in Notion

$('#GetFile').on('click', function () {
$.ajax({
url: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/172905/test.pdf',
method: 'GET',
xhrFields: {
responseType: 'blob'
},
success: function (response) {
var blob = response.data;
var a = document.createElement('a');
var fileUrl = window.URL.createObjectURL(blob);
var filename = 'myfile.pdf';
/* you can also use below to get filename from backend */
// var contentDisposition = response.headers('Content-Disposition');
// var matches = /filename=\\"(.+?)\\"/g.exec(contentDisposition);
// var filename = matches && matches.length > 1 ? matches\[1\] : '';
/* but need add backend code like below, otherwise you cannot get Content-Disposition header */
// + response.setHeader("Content-Disposition", "attachment; filename=\\"myfile.csv\\"");
// + response.setHeader("Content-type", "application/octet-stream;charset=utf-8");
// + response.setHeader("Access-Control-Expose-Headers","Content-Disposition");
// work in IE
if (window.navigator && window.navigator.msSaveBlob) {
window.navigator.msSaveBlob(blob, filename);
return;
}
a.href = fileUrl;
a.download = filename;
a.dispatchEvent(new MouseEvent('click'));
setTimeout(function() {
a.remove();
window.URL.revokeObjectURL.bind(window.URL, fileUrl);
});
}
});
});

Use @semantic to release package to NPM and publish docs to gh-pages automatically

· 2 min read

Open in Notion

  1. Install requisite dependencies

    # run local scripts for executing ts file for deploying docs
    npm i -D @types/node
    npm i -D ts-node

    # semantic-release and plugin for attaching version number to package.json
    npm i -D semantic-release
    npm i -D @semantic-release/git

    # git hooks and validate commit messages
    npm i -D husky
    npm i -D @commitlint/cli
  2. Add content to package.json

    \{
    "scripts": \{
    "deploy-docs": "ts-node tools/gh-pages-publish.ts",
    "semantic-release": "semantic-release"
    \},
    "files": [
    "dist"
    ],
    // if package scoped like @bndynet/ui, by default private, so...
    "publishConfig": \{
    "access": "public"
    \},
    "husky": \{
    "hooks": \{
    "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
    \}
    \}
    \}
  3. New file .releaserc (Release Configurations)

    \{
    "branch": "master",
    "prepare": [
    "@semantic-release/npm",
    \{
    "path": "@semantic-release/git",
    "assets": [
    "package.json",
    "package-lock.json"
    ],
    "message": "chore(release): $\{nextRelease.version\} by CI\n\n$\{nextRelease.notes\}"
    \}
    ],
    "plugins": [
    "@semantic-release/commit-analyzer",
    "@semantic-release/release-notes-generator",
    "@semantic-release/npm",
    "@semantic-release/github",
    "@semantic-release/git"
    ]
    \}
  4. New file .travis.yml (CI configurations for publishing docs and releasing package)

    language: node_js
    cache:
    directories:
    - ~/.npm
    notifications:
    email: false
    node_js:
    - '10'
    script:
    - npm run build
    after_success:
    - if [ "$TRAVIS_BRANCH" = "master" -a "$TRAVIS_PULL_REQUEST" = "false" ]; then npm run deploy-docs; fi
    - if [ "$TRAVIS_BRANCH" = "master" -a "$TRAVIS_PULL_REQUEST" = "false" ]; then npm run semantic-release; fi
    branches:
    except:
    - /^v\d+\.\d+\.\d+$/
  5. New file tools/gh-pages-publish.ts (Scripts to upload docs to gh-pages branch)

    import \{ cd, exec, echo, touch \} from "shelljs";
    import \{ readFileSync \} from "fs";
    import \{ parse \} from "url";

    let repoUrl
    let pkg = JSON.parse(readFileSync("package.json"))
    if (typeof pkg.repository === "object") \{
    if (!pkg.repository.hasOwnProperty("url")) \{
    throw new Error("URL does not exist in repository section")
    \}
    repoUrl = pkg.repository.url
    \} else \{
    repoUrl = pkg.repository
    \}

    let parsedUrl = parse(repoUrl)
    let repository = (parsedUrl.host || "") + (parsedUrl.path || "")
    let ghToken = process.env.GH_TOKEN

    echo("Deploying docs!!!")
    cd("docs")
    touch(".nojekyll")
    exec("git init")
    exec("git add .")
    exec('git config user.name "Bendy Zhang"')
    exec('git config user.email "zb@bndy.net"')
    exec('git commit -m "docs(docs): update gh-pages"')
    exec(
    `git push --force --quiet "https://${ghToken}@${repository}" master:gh-pages`
    )
    echo("Docs deployed!!")
  6. Add environment variables to CI (Travis CI)

    • GH_TOKEN or GITHUB_TOKEN: your token generated in GitHub and has repo scopes
    • NPM_TOKEN: your token generated in NPM